coverage-report

Code coverage report for All files

npmtest-node-red (v0.0.2)

Code coverage report for All files

Statements: 22.54% (2836 / 12581)      Branches: 8.1% (597 / 7371)      Functions: 16.85% (282 / 1674)      Lines: 23.21% (2826 / 12174)      Ignored: 26 statements, 1 function, 59 branches     

File Statements Branches Functions Lines
node-npmtest-node-red/ 100% (153 / 153) 100% (126 / 126) 100% (28 / 28) 100% (153 / 153)
node-npmtest-node-red/node_modules/node-red-node-email/ 7.26% (17 / 234) 1.27% (2 / 158) 2.94% (1 / 34) 7.76% (17 / 219)
node-npmtest-node-red/node_modules/node-red-node-feedparser/ 14.63% (6 / 41) 0% (0 / 22) 9.09% (1 / 11) 16.22% (6 / 37)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/async/dist/ 34.42% (444 / 1290) 10.13% (64 / 632) 7.91% (22 / 278) 35.58% (443 / 1245)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/bl/ 17.5% (21 / 120) 0% (0 / 61) 10.53% (2 / 19) 17.5% (21 / 120)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/caseless/ 15.09% (8 / 53) 0% (0 / 22) 0% (0 / 13) 17.39% (8 / 46)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/ 16.32% (31 / 190) 0% (0 / 121) 0% (0 / 25) 16.32% (31 / 190)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/ 55.56% (15 / 27) 0% (0 / 8) 28.57% (2 / 7) 55.56% (15 / 27)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/schemas/ 100% (21 / 21) 100% (0 / 0) 100% (0 / 0) 100% (21 / 21)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/node-uuid/ 31.67% (38 / 120) 10.34% (9 / 87) 23.08% (3 / 13) 31.62% (37 / 117)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/ 11.24% (28 / 249) 0.83% (2 / 241) 5.56% (1 / 18) 11.24% (28 / 249)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/ 100% (1 / 1) 100% (0 / 0) 100% (0 / 0) 100% (1 / 1)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/ 15.93% (134 / 841) 2.13% (11 / 516) 3.33% (3 / 90) 18% (133 / 739)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/ 7.88% (66 / 838) 0.16% (1 / 641) 1.3% (1 / 77) 7.96% (66 / 829)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/ 17.44% (101 / 579) 0.25% (1 / 397) 3.95% (3 / 76) 17.57% (101 / 575)
node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/tunnel-agent/ 19.42% (27 / 139) 5.88% (2 / 34) 0% (0 / 23) 19.57% (27 / 138)
node-npmtest-node-red/node_modules/node-red-node-rbe/ 5.08% (3 / 59) 0% (0 / 64) 33.33% (1 / 3) 6% (3 / 50)
node-npmtest-node-red/node_modules/node-red-node-twitter/ 5.19% (15 / 289) 0% (0 / 152) 2.44% (1 / 41) 5.47% (15 / 274)
node-npmtest-node-red/node_modules/node-red/ 44.83% (78 / 174) 41.8% (51 / 122) 26.67% (4 / 15) 45.03% (77 / 171)
node-npmtest-node-red/node_modules/node-red/nodes/core/analysis/ 33.33% (4 / 12) 0% (0 / 4) 25% (1 / 4) 33.33% (4 / 12)
node-npmtest-node-red/node_modules/node-red/nodes/core/core/ 11.25% (82 / 729) 4.93% (22 / 446) 14.43% (14 / 97) 11.99% (82 / 684)
node-npmtest-node-red/node_modules/node-red/nodes/core/hardware/ 6.44% (15 / 233) 0.85% (1 / 117) 3.45% (1 / 29) 6.8% (14 / 206)
node-npmtest-node-red/node_modules/node-red/nodes/core/io/ 6.6% (86 / 1304) 0.62% (5 / 810) 5.3% (8 / 151) 6.91% (86 / 1245)
node-npmtest-node-red/node_modules/node-red/nodes/core/logic/ 4.2% (18 / 429) 0% (0 / 336) 12.5% (4 / 32) 4.29% (18 / 420)
node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/ 7.54% (19 / 252) 0.54% (1 / 185) 27.78% (5 / 18) 8.3% (19 / 229)
node-npmtest-node-red/node_modules/node-red/nodes/core/storage/ 11.02% (13 / 118) 2.44% (2 / 82) 10% (2 / 20) 12.26% (13 / 106)
node-npmtest-node-red/node_modules/node-red/public/red/ 4.86% (7 / 144) 0% (0 / 42) 5% (1 / 20) 4.86% (7 / 144)
node-npmtest-node-red/node_modules/node-red/red/ 78% (39 / 50) 50% (8 / 16) 81.82% (9 / 11) 77.55% (38 / 49)
node-npmtest-node-red/node_modules/node-red/red/api/ 31.11% (247 / 794) 6.71% (22 / 328) 22.02% (24 / 109) 31.11% (247 / 794)
node-npmtest-node-red/node_modules/node-red/red/api/auth/ 32.03% (82 / 256) 2.94% (3 / 102) 9.8% (5 / 51) 31.89% (81 / 254)
node-npmtest-node-red/node_modules/node-red/red/runtime/ 40.29% (193 / 479) 19.94% (62 / 311) 58.67% (44 / 75) 40% (190 / 475)
node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/ 28.9% (113 / 391) 13.33% (24 / 180) 17.78% (8 / 45) 28.9% (113 / 391)
node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/ 18.17% (153 / 842) 3.81% (21 / 551) 17.05% (15 / 88) 18.3% (153 / 836)
node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/ 57.23% (459 / 802) 42.34% (141 / 333) 54.64% (53 / 97) 57.38% (459 / 800)
node-npmtest-node-red/node_modules/node-red/red/runtime/storage/ 30.18% (99 / 328) 12.9% (16 / 124) 26.79% (15 / 56) 30.18% (99 / 328)
Code coverage report for node-npmtest-node-red/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/

Statements: 100% (153 / 153)      Branches: 100% (126 / 126)      Functions: 100% (28 / 28)      Lines: 100% (153 / 153)      Ignored: 26 statements, 1 function, 30 branches     

All files » node-npmtest-node-red/
File Statements Branches Functions Lines
example.js 100% (83 / 83) 100% (73 / 73) 100% (12 / 12) 100% (83 / 83)
lib.npmtest_node_red.js 100% (16 / 16) 100% (14 / 14) 100% (3 / 3) 100% (16 / 16)
test.js 100% (54 / 54) 100% (39 / 39) 100% (13 / 13) 100% (54 / 54)
Code coverage report for node-npmtest-node-red/example.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/example.js

Statements: 100% (83 / 83)      Branches: 100% (73 / 73)      Functions: 100% (12 / 12)      Lines: 100% (83 / 83)      Ignored: 26 statements, 1 function, 30 branches     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328                                                  2   2         2   2   2 2 2         1             2       2       2   2               1 2           2     2     2 2   1       2     1 1 1   1 1     1 1   1     1   2           1   1   1         1 2 2 3 3 3 3 1     3 3         3       1 3 1       1 1               1   1 1 1   1   1                                                                                                                                                                                       1 1                       1     6 6   1   2   1   2         1 1   1         1             1     1 1     1 1   1 1 1 1 1 1 1   1 1   1        
/*
example.js
 
quickstart example
 
instruction
    1. save this script as example.js
    2. run the shell command:
        $ npm install npmtest-node-red && PORT=8081 node example.js
    3. play with the browser-demo on http://127.0.0.1:8081
*/
 
 
 
/* istanbul instrument in package npmtest_node_red */
/*jslint
    bitwise: true,
    browser: true,
    maxerr: 8,
    maxlen: 96,
    node: true,
    nomen: true,
    regexp: true,
    stupid: true
*/
(function () {
    'use strict';
    var local;
 
 
 
    // run shared js-env code - init-before
    (function () {
        // init local
        local = {};
        // init modeJs
        local.modeJs = (function () {
            try {
                return typeof navigator.userAgent === 'string' &&
                    typeof document.querySelector('body') === 'object' &&
                    typeof XMLHttpRequest.prototype.open === 'function' &&
                    'browser';
            } catch (errorCaughtBrowser) {
                return module.exports &&
                    typeof process.versions.node === 'string' &&
                    typeof require('http').createServer === 'function' &&
                    'node';
            }
        }());
        // init global
        local.global = local.modeJs === 'browser'
            ? window
            : global;
        // init utility2_rollup
        local = local.global.utility2_rollup || (local.modeJs === 'browser'
            ? local.global.utility2_npmtest_node_red
            : global.utility2_moduleExports);
        // export local
        local.global.local = local;
    }());
    switch (local.modeJs) {
 
 
 
    // init-after
    // run browser js-env code - init-after
    /* istanbul ignore next */
    case 'browser':
        local.testRunBrowser = function (event) {
            Eif (!event || (event &&
                    event.currentTarget &&
                    event.currentTarget.className &&
                    event.currentTarget.className.includes &&
                    event.currentTarget.className.includes('onreset'))) {
                // reset output
                Array.from(
                    document.querySelectorAll('body > .resettable')
                ).forEach(function (element) {
                    switch (element.tagName) {
                    case 'INPUT':
                    case 'TEXTAREA':
                        element.value = '';
                        break;
                    default:
                        element.textContent = '';
                    }
                });
            }
            switch (event && event.currentTarget && event.currentTarget.id) {
            case 'testRunButton1':
                // show tests
                Eif (document.querySelector('#testReportDiv1').style.display === 'none') {
                    document.querySelector('#testReportDiv1').style.display = 'block';
                    document.querySelector('#testRunButton1').textContent =
                        'hide internal test';
                    local.modeTest = true;
                    local.testRunDefault(local);
                // hide tests
                } else {
                    document.querySelector('#testReportDiv1').style.display = 'none';
                    document.querySelector('#testRunButton1').textContent = 'run internal test';
                }
                break;
            // custom-case
            default:
                break;
            }
            Iif (document.querySelector('#inputTextareaEval1') && (!event || (event &&
                    event.currentTarget &&
                    event.currentTarget.className &&
                    event.currentTarget.className.includes &&
                    event.currentTarget.className.includes('oneval')))) {
                // try to eval input-code
                try {
                    /*jslint evil: true*/
                    eval(document.querySelector('#inputTextareaEval1').value);
                } catch (errorCaught) {
                    console.error(errorCaught);
                }
            }
        };
        // log stderr and stdout to #outputTextareaStdout1
        ['error', 'log'].forEach(function (key) {
            console[key + '_original'] = console[key];
            console[key] = function () {
                var element;
                console[key + '_original'].apply(console, arguments);
                element = document.querySelector('#outputTextareaStdout1');
                Iif (!element) {
                    return;
                }
                // append text to #outputTextareaStdout1
                element.value += Array.from(arguments).map(function (arg) {
                    return typeof arg === 'string'
                        ? arg
                        : JSON.stringify(arg, null, 4);
                }).join(' ') + '\n';
                // scroll textarea to bottom
                element.scrollTop = element.scrollHeight;
            };
        });
        // init event-handling
        ['change', 'click', 'keyup'].forEach(function (event) {
            Array.from(document.querySelectorAll('.on' + event)).forEach(function (element) {
                element.addEventListener(event, local.testRunBrowser);
            });
        });
        // run tests
        local.testRunBrowser();
        break;
 
 
 
    // run node js-env code - init-after
    /* istanbul ignore next */
    case 'node':
        // export local
        module.exports = local;
        // require modules
        local.fs = require('fs');
        local.http = require('http');
        local.url = require('url');
        // init assets
        local.assetsDict = local.assetsDict || {};
        /* jslint-ignore-begin */
        local.assetsDict['/assets.index.template.html'] = '\
<!doctype html>\n\
<html lang="en">\n\
<head>\n\
<meta charset="UTF-8">\n\
<meta name="viewport" content="width=device-width, initial-scale=1">\n\
<title>{{env.npm_package_name}} (v{{env.npm_package_version}})</title>\n\
<style>\n\
/*csslint\n\
    box-sizing: false,\n\
    universal-selector: false\n\
*/\n\
* {\n\
    box-sizing: border-box;\n\
}\n\
body {\n\
    background: #dde;\n\
    font-family: Arial, Helvetica, sans-serif;\n\
    margin: 2rem;\n\
}\n\
body > * {\n\
    margin-bottom: 1rem;\n\
}\n\
.utility2FooterDiv {\n\
    margin-top: 20px;\n\
    text-align: center;\n\
}\n\
</style>\n\
<style>\n\
/*csslint\n\
*/\n\
textarea {\n\
    font-family: monospace;\n\
    height: 10rem;\n\
    width: 100%;\n\
}\n\
textarea[readonly] {\n\
    background: #ddd;\n\
}\n\
</style>\n\
</head>\n\
<body>\n\
<!-- utility2-comment\n\
<div id="ajaxProgressDiv1" style="background: #d00; height: 2px; left: 0; margin: 0; padding: 0; position: fixed; top: 0; transition: background 0.5s, width 1.5s; width: 25%;"></div>\n\
utility2-comment -->\n\
<h1>\n\
<!-- utility2-comment\n\
    <a\n\
        {{#if env.npm_package_homepage}}\n\
        href="{{env.npm_package_homepage}}"\n\
        {{/if env.npm_package_homepage}}\n\
        target="_blank"\n\
    >\n\
utility2-comment -->\n\
        {{env.npm_package_name}} (v{{env.npm_package_version}})\n\
<!-- utility2-comment\n\
    </a>\n\
utility2-comment -->\n\
</h1>\n\
<h3>{{env.npm_package_description}}</h3>\n\
<!-- utility2-comment\n\
<h4><a download href="assets.app.js">download standalone app</a></h4>\n\
<button class="onclick onreset" id="testRunButton1">run internal test</button><br>\n\
<div id="testReportDiv1" style="display: none;"></div>\n\
utility2-comment -->\n\
\n\
\n\
\n\
<label>stderr and stdout</label>\n\
<textarea class="resettable" id="outputTextareaStdout1" readonly></textarea>\n\
<!-- utility2-comment\n\
{{#if isRollup}}\n\
<script src="assets.app.js"></script>\n\
{{#unless isRollup}}\n\
utility2-comment -->\n\
<script src="assets.utility2.rollup.js"></script>\n\
<script src="jsonp.utility2._stateInit?callback=window.utility2._stateInit"></script>\n\
<script src="assets.npmtest_node_red.rollup.js"></script>\n\
<script src="assets.example.js"></script>\n\
<script src="assets.test.js"></script>\n\
<!-- utility2-comment\n\
{{/if isRollup}}\n\
utility2-comment -->\n\
<div class="utility2FooterDiv">\n\
    [ this app was created with\n\
    <a href="https://github.com/kaizhu256/node-utility2" target="_blank">utility2</a>\n\
    ]\n\
</div>\n\
</body>\n\
</html>\n\
';
        /* jslint-ignore-end */
        Iif (local.templateRender) {
            local.assetsDict['/'] = local.templateRender(
                local.assetsDict['/assets.index.template.html'],
                {
                    env: local.objectSetDefault(local.env, {
                        npm_package_description: 'the greatest app in the world!',
                        npm_package_name: 'my-app',
                        npm_package_nameAlias: 'my_app',
                        npm_package_version: '0.0.1'
                    })
                }
            );
        } else {
            local.assetsDict['/'] = local.assetsDict['/assets.index.template.html']
                .replace((/\{\{env\.(\w+?)\}\}/g), function (match0, match1) {
                    // jslint-hack
                    String(match0);
                    switch (match1) {
                    case 'npm_package_description':
                        return 'the greatest app in the world!';
                    case 'npm_package_name':
                        return 'my-app';
                    case 'npm_package_nameAlias':
                        return 'my_app';
                    case 'npm_package_version':
                        return '0.0.1';
                    }
                });
        }
        // run the cli
        Eif (local.global.utility2_rollup || module !== require.main) {
            break;
        }
        local.assetsDict['/assets.example.js'] =
            local.assetsDict['/assets.example.js'] ||
            local.fs.readFileSync(__filename, 'utf8');
        // bug-workaround - long $npm_package_buildCustomOrg
        /* jslint-ignore-begin */
        local.assetsDict['/assets.npmtest_node_red.rollup.js'] =
            local.assetsDict['/assets.npmtest_node_red.rollup.js'] ||
            local.fs.readFileSync(
                local.npmtest_node_red.__dirname + '/lib.npmtest_node_red.js',
                'utf8'
            ).replace((/^#!/), '//');
        /* jslint-ignore-end */
        local.assetsDict['/favicon.ico'] = local.assetsDict['/favicon.ico'] || '';
        // if $npm_config_timeout_exit exists,
        // then exit this process after $npm_config_timeout_exit ms
        if (Number(process.env.npm_config_timeout_exit)) {
            setTimeout(process.exit, Number(process.env.npm_config_timeout_exit));
        }
        // start server
        if (local.global.utility2_serverHttp1) {
            break;
        }
        process.env.PORT = process.env.PORT || '8081';
        console.error('server starting on port ' + process.env.PORT);
        local.http.createServer(function (request, response) {
            request.urlParsed = local.url.parse(request.url);
            if (local.assetsDict[request.urlParsed.pathname] !== undefined) {
                response.end(local.assetsDict[request.urlParsed.pathname]);
                return;
            }
            response.statusCode = 404;
            response.end();
        }).listen(process.env.PORT);
        break;
    }
}());
 
 
Code coverage report for node-npmtest-node-red/lib.npmtest_node_red.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/lib.npmtest_node_red.js

Statements: 100% (16 / 16)      Branches: 100% (14 / 14)      Functions: 100% (3 / 3)      Lines: 100% (16 / 16)      Ignored: none     

All files » node-npmtest-node-red/ » lib.npmtest_node_red.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55                      2   2         2   2   2 2 2         1             2       2   2   2 1   1 1 1          
/* istanbul instrument in package npmtest_node_red */
/*jslint
    bitwise: true,
    browser: true,
    maxerr: 8,
    maxlen: 96,
    node: true,
    nomen: true,
    regexp: true,
    stupid: true
*/
(function () {
    'use strict';
    var local;
 
 
 
    // run shared js-env code - init-before
    (function () {
        // init local
        local = {};
        // init modeJs
        local.modeJs = (function () {
            try {
                return typeof navigator.userAgent === 'string' &&
                    typeof document.querySelector('body') === 'object' &&
                    typeof XMLHttpRequest.prototype.open === 'function' &&
                    'browser';
            } catch (errorCaughtBrowser) {
                return module.exports &&
                    typeof process.versions.node === 'string' &&
                    typeof require('http').createServer === 'function' &&
                    'node';
            }
        }());
        // init global
        local.global = local.modeJs === 'browser'
            ? window
            : global;
        // init utility2_rollup
        local = local.global.utility2_rollup || local;
        // init lib
        local.local = local.npmtest_node_red = local;
        // init exports
        if (local.modeJs === 'browser') {
            local.global.utility2_npmtest_node_red = local;
        } else {
            module.exports = local;
            module.exports.__dirname = __dirname;
            module.exports.module = module;
        }
    }());
}());
 
 
Code coverage report for node-npmtest-node-red/test.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/test.js

Statements: 100% (54 / 54)      Branches: 100% (39 / 39)      Functions: 100% (13 / 13)      Lines: 100% (54 / 54)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197                      2   2         2   2   2 2 2         1             2     2     1       1     1   1     2           2 2   2           1           1           2 2   2           1             1       1     1             1             1 1     1             1 1 1 1 1 1     1         2 2     1             2 2     1             2 2     1             2 2     1             1 1       1 1        
/* istanbul instrument in package npmtest_node_red */
/*jslint
    bitwise: true,
    browser: true,
    maxerr: 8,
    maxlen: 96,
    node: true,
    nomen: true,
    regexp: true,
    stupid: true
*/
(function () {
    'use strict';
    var local;
 
 
 
    // run shared js-env code - init-before
    (function () {
        // init local
        local = {};
        // init modeJs
        local.modeJs = (function () {
            try {
                return typeof navigator.userAgent === 'string' &&
                    typeof document.querySelector('body') === 'object' &&
                    typeof XMLHttpRequest.prototype.open === 'function' &&
                    'browser';
            } catch (errorCaughtBrowser) {
                return module.exports &&
                    typeof process.versions.node === 'string' &&
                    typeof require('http').createServer === 'function' &&
                    'node';
            }
        }());
        // init global
        local.global = local.modeJs === 'browser'
            ? window
            : global;
        switch (local.modeJs) {
        // re-init local from window.local
        case 'browser':
            local = local.global.utility2.objectSetDefault(
                local.global.utility2_rollup || local.global.local,
                local.global.utility2
            );
            break;
        // re-init local from example.js
        case 'node':
            local = (local.global.utility2_rollup || require('utility2'))
                .requireReadme();
            break;
        }
        // export local
        local.global.local = local;
    }());
 
 
 
    // run shared js-env code - function
    (function () {
        return;
    }());
    switch (local.modeJs) {
 
 
 
    // run browser js-env code - function
    case 'browser':
        break;
 
 
 
    // run node js-env code - function
    case 'node':
        break;
    }
 
 
 
    // run shared js-env code - init-after
    (function () {
        return;
    }());
    switch (local.modeJs) {
 
 
 
    // run browser js-env code - init-after
    case 'browser':
        local.testCase_browser_nullCase = local.testCase_browser_nullCase || function (
            options,
            onError
        ) {
        /*
         * this function will test browsers's null-case handling-behavior-behavior
         */
            onError(null, options);
        };
 
        // run tests
        local.nop(local.modeTest &&
            document.querySelector('#testRunButton1') &&
            document.querySelector('#testRunButton1').click());
        break;
 
 
 
    // run node js-env code - init-after
    /* istanbul ignore next */
    case 'node':
        local.testCase_buildApidoc_default = local.testCase_buildApidoc_default || function (
            options,
            onError
        ) {
        /*
         * this function will test buildApidoc's default handling-behavior-behavior
         */
            options = { modulePathList: module.paths };
            local.buildApidoc(options, onError);
        };
 
        local.testCase_buildApp_default = local.testCase_buildApp_default || function (
            options,
            onError
        ) {
        /*
         * this function will test buildApp's default handling-behavior-behavior
         */
            local.testCase_buildReadme_default(options, local.onErrorThrow);
            local.testCase_buildLib_default(options, local.onErrorThrow);
            local.testCase_buildTest_default(options, local.onErrorThrow);
            local.testCase_buildCustomOrg_default(options, local.onErrorThrow);
            options = [];
            local.buildApp(options, onError);
        };
 
        local.testCase_buildCustomOrg_default = local.testCase_buildCustomOrg_default ||
            function (options, onError) {
            /*
             * this function will test buildCustomOrg's default handling-behavior
             */
                options = {};
                local.buildCustomOrg(options, onError);
            };
 
        local.testCase_buildLib_default = local.testCase_buildLib_default || function (
            options,
            onError
        ) {
        /*
         * this function will test buildLib's default handling-behavior
         */
            options = {};
            local.buildLib(options, onError);
        };
 
        local.testCase_buildReadme_default = local.testCase_buildReadme_default || function (
            options,
            onError
        ) {
        /*
         * this function will test buildReadme's default handling-behavior-behavior
         */
            options = {};
            local.buildReadme(options, onError);
        };
 
        local.testCase_buildTest_default = local.testCase_buildTest_default || function (
            options,
            onError
        ) {
        /*
         * this function will test buildTest's default handling-behavior
         */
            options = {};
            local.buildTest(options, onError);
        };
 
        local.testCase_webpage_default = local.testCase_webpage_default || function (
            options,
            onError
        ) {
        /*
         * this function will test webpage's default handling-behavior
         */
            options = { modeCoverageMerge: true, url: local.serverLocalHost + '?modeTest=1' };
            local.browserTest(options, onError);
        };
 
        // run test-server
        local.testRunServer(local);
        break;
    }
}());
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-email/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-email/

Statements: 7.26% (17 / 234)      Branches: 1.27% (2 / 158)      Functions: 2.94% (1 / 34)      Lines: 7.76% (17 / 219)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-email/
File Statements Branches Functions Lines
61-email.js 7.26% (17 / 234) 1.27% (2 / 158) 2.94% (1 / 34) 7.76% (17 / 219)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-email/61-email.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-email/61-email.js

Statements: 7.26% (17 / 234)      Branches: 1.27% (2 / 158)      Functions: 2.94% (1 / 34)      Lines: 7.76% (17 / 219)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440                      1   1 1 1 1 1   1 1         1                                                                                                                                                                                               1                         1                                                                                             1                                         1                       1                                                                                                                                                                       1                                                                                                                                                                       1                                                                                               1                  
 
/**
 * POP3 protocol - RFC1939 - https://www.ietf.org/rfc/rfc1939.txt
 *
 * Dependencies:
 * * poplib     - https://www.npmjs.com/package/poplib
 * * nodemailer - https://www.npmjs.com/package/nodemailer
 * * imap       - https://www.npmjs.com/package/imap
 * * mailparser - https://www.npmjs.com/package/mailparser
 */
 
module.exports = function(RED) {
    "use strict";
    var nodemailer = require("nodemailer");
    var Imap = require('imap');
    var POP3Client = require("poplib");
    var MailParser = require("mailparser").MailParser;
    var util = require("util");
 
    try {
        var globalkeys = RED.settings.email || require(process.env.NODE_RED_HOME+"/../emailkeys.js");
    }
    catch(err) {
    }
 
    function EmailNode(n) {
        RED.nodes.createNode(this,n);
        this.topic = n.topic;
        this.name = n.name;
        this.outserver = n.server;
        this.outport = n.port;
        this.secure = n.secure;
        var flag = false;
        if (this.credentials && this.credentials.hasOwnProperty("userid")) {
            this.userid = this.credentials.userid;
        } else {
            if (globalkeys) {
                this.userid = globalkeys.user;
                flag = true;
            }
        }
        if (this.credentials && this.credentials.hasOwnProperty("password")) {
            this.password = this.credentials.password;
        } else {
            if (globalkeys) {
                this.password = globalkeys.pass;
                flag = true;
            }
        }
        if (flag) {
            RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true});
        }
        var node = this;
 
        var smtpOptions = {
            host: node.outserver,
            port: node.outport,
            secure: node.secure
        }
 
        if (this.userid && this.password) {
            smtpOptions.auth = {
                user: node.userid,
                pass: node.password
            };
        }
        var smtpTransport = nodemailer.createTransport(smtpOptions);
 
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                if (smtpTransport) {
                    node.status({fill:"blue",shape:"dot",text:"email.status.sending"});
                    if (msg.to && node.name && (msg.to !== node.name)) {
                        node.warn(RED._("node-red:common.errors.nooverride"));
                    }
                    var sendopts = { from: ((msg.from) ? msg.from : node.userid) };   // sender address
                    sendopts.to = node.name || msg.to; // comma separated list of addressees
                    if (node.name === "") {
                        sendopts.cc = msg.cc;
                        sendopts.bcc = msg.bcc;
                    }
                    sendopts.subject = msg.topic || msg.title || "Message from Node-RED"; // subject line
                    if (msg.hasOwnProperty("envelope")) { sendopts.envelope = msg.envelope; }
                    if (Buffer.isBuffer(msg.payload)) { // if it's a buffer in the payload then auto create an attachment instead
                        if (!msg.filename) {
                            var fe = "bin";
                            if ((msg.payload[0] === 0xFF)&&(msg.payload[1] === 0xD8)) { fe = "jpg"; }
                            if ((msg.payload[0] === 0x47)&&(msg.payload[1] === 0x49)) { fe = "gif"; } //46
                            if ((msg.payload[0] === 0x42)&&(msg.payload[1] === 0x4D)) { fe = "bmp"; }
                            if ((msg.payload[0] === 0x89)&&(msg.payload[1] === 0x50)) { fe = "png"; } //4E
                            msg.filename = "attachment."+fe;
                        }
                        var fname = msg.filename.replace(/^.*[\\\/]/, '') || "file.bin";
                        sendopts.attachments = [ { content:msg.payload, filename:fname } ];
                        if (msg.hasOwnProperty("headers") && msg.headers.hasOwnProperty("content-type")) {
                            sendopts.attachments[0].contentType = msg.headers["content-type"];
                        }
                        // Create some body text..
                        sendopts.text = RED._("email.default-message",{filename:fname, description:(msg.description||"")});
                    }
                    else {
                        var payload = RED.util.ensureString(msg.payload);
                        sendopts.text = payload; // plaintext body
                        if (/<[a-z][\s\S]*>/i.test(payload)) { sendopts.html = payload; } // html body
                        if (msg.attachments) { sendopts.attachments = msg.attachments; } // add attachments
                    }
                    smtpTransport.sendMail(sendopts, function(error, info) {
                        if (error) {
                            node.error(error,msg);
                            node.status({fill:"red",shape:"ring",text:"email.status.sendfail"});
                        } else {
                            node.log(RED._("email.status.messagesent",{response:info.response}));
                            node.status({});
                        }
                    });
                }
                else { node.warn(RED._("email.errors.nosmtptransport")); }
            }
            else { node.warn(RED._("email.errors.nopayload")); }
        });
    }
    RED.nodes.registerType("e-mail",EmailNode,{
        credentials: {
            userid: {type:"text"},
            password: {type: "password"},
            global: { type:"boolean"}
        }
    });
 
 
    //
    // EmailInNode
    //
    // Setup the EmailInNode
    function EmailInNode(n) {
        var imap;
 
        RED.nodes.createNode(this,n);
        this.name = n.name;
        this.repeat = n.repeat * 1000 || 300000;
        this.inserver = n.server || (globalkeys && globalkeys.server) || "imap.gmail.com";
        this.inport = n.port || (globalkeys && globalkeys.port) || "993";
        this.box = n.box || "INBOX";
        this.useSSL= n.useSSL;
        this.protocol = n.protocol || "IMAP";
        this.disposition = n.disposition || "None"; // "None", "Delete", "Read"
 
        var flag = false;
 
        if (this.credentials && this.credentials.hasOwnProperty("userid")) {
            this.userid = this.credentials.userid;
        } else {
            if (globalkeys) {
                this.userid = globalkeys.user;
                flag = true;
            } else {
                this.error(RED._("email.errors.nouserid"));
            }
        }
        if (this.credentials && this.credentials.hasOwnProperty("password")) {
            this.password = this.credentials.password;
        } else {
            if (globalkeys) {
                this.password = globalkeys.pass;
                flag = true;
            } else {
                this.error(RED._("email.errors.nopassword"));
            }
        }
        if (flag) {
            RED.nodes.addCredentials(n.id,{userid:this.userid, password:this.password, global:true});
        }
 
        var node = this;
        this.interval_id = null;
 
        // Process a new email message by building a Node-RED message to be passed onwards
        // in the message flow.  The parameter called `msg` is the template message we
        // start with while `mailMessage` is an object returned from `mailparser` that
        // will be used to populate the email.
        // DCJ NOTE: - heirachical multipart mime parsers seem to not exist - this one is barely functional.
        function processNewMessage(msg, mailMessage) {
            msg = JSON.parse(JSON.stringify(msg)); // Clone the message
            // Populate the msg fields from the content of the email message
            // that we have just parsed.
            msg.payload = mailMessage.text;
            msg.topic = mailMessage.subject;
            msg.date = mailMessage.date;
            msg.header = mailMessage.headers;
            if (mailMessage.html) { msg.html = mailMessage.html; }
            if (mailMessage.to && mailMessage.from.to > 0) { msg.to = mailMessage.to; }
            if (mailMessage.cc && mailMessage.from.cc > 0) { msg.cc = mailMessage.cc; }
            if (mailMessage.bcc && mailMessage.from.bcc > 0) { msg.bcc = mailMessage.bcc; }
            if (mailMessage.from && mailMessage.from.length > 0) { msg.from = mailMessage.from[0].address; }
            if (mailMessage.attachments) { msg.attachments = mailMessage.attachments; }
            else { msg.attachments = []; }
            node.send(msg); // Propagate the message down the flow
        } // End of processNewMessage
 
        // Check the POP3 email mailbox for any new messages.  For any that are found,
        // retrieve each message, call processNewMessage to process it and then delete
        // the messages from the server.
        function checkPOP3(msg) {
            var currentMessage;
            var maxMessage;
 
            // Form a new connection to our email server using POP3.
            var pop3Client = new POP3Client(
                node.inport, node.inserver,
                {enabletls: node.useSSL} // Should we use SSL to connect to our email server?
            );
 
            // If we have a next message to retrieve, ask to retrieve it otherwise issue a
            // quit request.
            function nextMessage() {
                if (currentMessage > maxMessage) {
                    pop3Client.quit();
                    return;
                }
                pop3Client.retr(currentMessage);
                currentMessage++;
            } // End of nextMessage
 
            pop3Client.on("stat", function(status, data) {
                // Data contains:
                // {
                //   count: <Number of messages to be read>
                //   octect: <size of messages to be read>
                // }
                if (status) {
                    currentMessage = 1;
                    maxMessage = data.count;
                    nextMessage();
                } else {
                    node.log(util.format("stat error: %s %j", status, data));
                }
            });
 
            pop3Client.on("error", function(err) {
                node.log("We caught an error: " + JSON.stringify(err));
            });
 
            pop3Client.on("connect", function() {
                //node.log("We are now connected");
                pop3Client.login(node.userid, node.password);
            });
 
            pop3Client.on("login", function(status, rawData) {
                //node.log("login: " + status + ", " + rawData);
                if (status) {
                    pop3Client.stat();
                } else {
                    node.log(util.format("login error: %s %j", status, rawData));
                    pop3Client.quit();
                }
            });
 
            pop3Client.on("retr", function(status, msgNumber, data, rawData) {
                // node.log(util.format("retr: status=%s, msgNumber=%d, data=%j", status, msgNumber, data));
                if (status) {
 
                    // We have now received a new email message.  Create an instance of a mail parser
                    // and pass in the email message.  The parser will signal when it has parsed the message.
                    var mailparser = new MailParser();
                    mailparser.on("end", function(mailObject) {
                        //node.log(util.format("mailparser: on(end): %j", mailObject));
                        processNewMessage(msg, mailObject);
                    });
                    mailparser.write(data);
                    mailparser.end();
                    pop3Client.dele(msgNumber);
                }
                else {
                    node.log(util.format("retr error: %s %j", status, rawData));
                    pop3Client.quit();
                }
            });
 
            pop3Client.on("invalid-state", function(cmd) {
                node.log("Invalid state: " + cmd);
            });
 
            pop3Client.on("locked", function(cmd) {
                node.log("We were locked: " + cmd);
            });
 
            // When we have deleted the last processed message, we can move on to
            // processing the next message.
            pop3Client.on("dele", function(status, msgNumber) {
                nextMessage();
            });
        } // End of checkPOP3
 
 
        //
        // checkIMAP
        //
        // Check the email sever using the IMAP protocol for new messages.
        function checkIMAP(msg) {
            node.log("Checking IMAP for new messages");
            // We get back a 'ready' event once we have connected to imap
            imap.once("ready", function() {
                node.status({fill:"blue", shape:"dot", text:"email.status.fetching"});
                //console.log("> ready");
                // Open the inbox folder
                imap.openBox(node.box, // Mailbox name
                    false, // Open readonly?
                    function(err, box) {
                    //console.log("> Inbox open: %j", box);
                    imap.search([ 'UNSEEN' ], function(err, results) {
                        if (err) {
                            node.status({fill:"red", shape:"ring", text:"email.status.foldererror"});
                            node.error(RED._("email.errors.fetchfail", {folder:node.box}),err);
                            imap.end();
                            return;
                        }
                        //console.log("> search - err=%j, results=%j", err, results);
                        if (results.length === 0) {
                            //console.log(" [X] - Nothing to fetch");
                            node.status({});
                            imap.end();
                            return;
                        }
 
                        var marks = false;
                        if (node.disposition === "Read") { marks = true; }
                        // We have the search results that contain the list of unseen messages and can now fetch those messages.
                        var fetch = imap.fetch(results, {
                            bodies: '',
                            struct: true,
                            markSeen: marks
                        });
 
                        // For each fetched message returned ...
                        fetch.on('message', function(imapMessage, seqno) {
                            //node.log(RED._("email.status.message",{number:seqno}));
                            var messageText = "";
                            //console.log("> Fetch message - msg=%j, seqno=%d", imapMessage, seqno);
                            imapMessage.on('body', function(stream, info) {
                                //console.log("> message - body - stream=?, info=%j", info);
                                stream.on('data', function(chunk) {
                                    //console.log("> stream - data - chunk=??");
                                    messageText += chunk.toString('utf8');
                                });
                                stream.once('end', function() {
                                    var mailParser = new MailParser();
                                    mailParser.on('end', function(mailMessage) {
                                        processNewMessage(msg, mailMessage);
                                    });
                                    mailParser.write(messageText);
                                    mailParser.end();
                                }); // End of msg->end
                            }); // End of msg->body
                        }); // End of fetch->message
 
                        // When we have fetched all the messages, we don't need the imap connection any more.
                        fetch.on('end', function() {
                            node.status({});
                            var cleanup = function() {
                                imap.end();
                            };
                            if (this.disposition === "Delete") {
                                imap.addFlags(results, "\Deleted", cleanup);
                            } else if (this.disposition === "Read") {
                                imap.addFlags(results, "\Seen", cleanup);
                            } else {
                                cleanup();
                            }
                        });
 
                        fetch.once('error', function(err) {
                            console.log('Fetch error: ' + err);
                        });
                    }); // End of imap->search
                }); // End of imap->openInbox
            }); // End of imap->ready
            node.status({fill:"grey",shape:"dot",text:"node-red:common.status.connecting"});
            imap.connect();
        } // End of checkIMAP
 
 
        // Perform a check of the email inboxes using either POP3 or IMAP
        function checkEmail(msg) {
            if (node.protocol === "POP3") {
                checkPOP3(msg);
            } else if (node.protocol === "IMAP") {
                checkIMAP(msg);
            }
        }  // End of checkEmail
 
        if (node.protocol === "IMAP") {
            imap = new Imap({
                user: node.userid,
                password: node.password,
                host: node.inserver,
                port: node.inport,
                tls: node.useSSL,
                tlsOptions: { rejectUnauthorized: false },
                connTimeout: node.repeat,
                authTimeout: node.repeat
            });
            imap.on('error', function(err) {
                if (err.errno !== "ECONNRESET") {
                    node.log(err);
                    node.status({fill:"red",shape:"ring",text:"email.status.connecterror"});
                }
            });
        }
 
        this.on("input", function(msg) {
            checkEmail(msg);
        });
 
        this.on("close", function() {
            if (this.interval_id != null) {
                clearInterval(this.interval_id);
            }
            if (imap) { imap.destroy(); }
        });
 
        // Set the repetition timer as needed
        if (!isNaN(this.repeat) && this.repeat > 0) {
            this.interval_id = setInterval( function() {
                node.emit("input",{});
            }, this.repeat );
        }
 
        node.emit("input",{});
    }
 
    RED.nodes.registerType("e-mail in",EmailInNode,{
        credentials: {
            userid: { type:"text" },
            password: { type: "password" },
            global: { type:"boolean" }
        }
    });
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/

Statements: 14.63% (6 / 41)      Branches: 0% (0 / 22)      Functions: 9.09% (1 / 11)      Lines: 16.22% (6 / 37)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/
File Statements Branches Functions Lines
32-feedparse.js 14.63% (6 / 41) 0% (0 / 22) 9.09% (1 / 11) 16.22% (6 / 37)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/32-feedparse.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/32-feedparse.js

Statements: 14.63% (6 / 41)      Branches: 0% (0 / 22)      Functions: 9.09% (1 / 11)      Lines: 16.22% (6 / 37)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68  1   1 1 1   1                                                                                                                 1      
 
module.exports = function(RED) {
    "use strict";
    var FeedParser = require("feedparser");
    var request = require("request");
    var url = require('url');
 
    function FeedParseNode(n) {
        RED.nodes.createNode(this,n);
        this.url = n.url;
        this.interval = (parseInt(n.interval)||15) * 60000;
        var node = this;
        this.interval_id = null;
        this.seen = {};
        var parsedUrl = url.parse(this.url);
        if (!(parsedUrl.host || (parsedUrl.hostname && parsedUrl.port)) && !parsedUrl.isUnix) {
            this.error(RED._("feedparse.errors.invalidurl"));
        } else {
            var getFeed = function() {
                var req = request(node.url, {timeout: 10000, pool: false});
                //req.setMaxListeners(50);
                //req.setHeader('user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36');
                //req.setHeader('accept', 'text/html,application/xhtml+xml');
 
                var feedparser = new FeedParser();
 
                req.on('error', function(err) { node.error(err); });
 
                req.on('response', function(res) {
                    if (res.statusCode != 200) { node.warn(RED._("feedparse.errors.badstatuscode")+" "+res.statusCode); }
                    else { res.pipe(feedparser); }
                });
 
                feedparser.on('error', function(error) { node.error(error); });
 
                feedparser.on('readable', function () {
                    var stream = this, article;
                    while (article = stream.read()) {  // jshint ignore:line
                        if (!(article.guid in node.seen) || ( node.seen[article.guid] !== 0 && node.seen[article.guid] != article.date.getTime())) {
                            node.seen[article.guid] = article.date?article.date.getTime():0;
                            var msg = {
                                topic: article.origlink || article.link,
                                payload: article.description,
                                article: article
                            };
                            node.send(msg);
                        }
                    }
                });
 
                feedparser.on('meta', function (meta) {});
                feedparser.on('end', function () {});
            };
            this.interval_id = setInterval(function() { getFeed(); }, node.interval);
            getFeed();
        }
 
        this.on("close", function() {
            if (this.interval_id != null) {
                clearInterval(this.interval_id);
            }
        });
    }
 
    RED.nodes.registerType("feedparse",FeedParseNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/async/dist/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/async/dist/

Statements: 34.42% (444 / 1290)      Branches: 10.13% (64 / 632)      Functions: 7.91% (22 / 278)      Lines: 35.58% (443 / 1245)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/async/dist/
File Statements Branches Functions Lines
async.js 34.42% (444 / 1290) 10.13% (64 / 632) 7.91% (22 / 278) 35.58% (443 / 1245)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/async/dist/async.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/async/dist/async.js

Statements: 34.42% (444 / 1290)      Branches: 10.13% (64 / 632)      Functions: 7.91% (22 / 278)      Lines: 35.58% (443 / 1245)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 54781 1                             1                     1                     1 10 10                                                                     1           1 10     1                                                               1                                                                                                                         1                                         1   1 1 1   1   1   1     1       1       1   1 2                               1     1     1     1     1     1             1     1                 1                                         1             1                 1 1       1 1     1                 1 1     1 1           1 1 1 1                                     1                     1                                                       1                                                           1           1                           1       1                 1   1                         1                                                                   1 1       1                 1 1       1     1     1                                       1                                                       1                             1         1     1     1     1     1                                     1     1     1                   1               1 1 1 1 1 1 1 1 1 1 1 1 1   1 1 1 1 1 1 1 1 1 1 1     1 1         1                               1                       1 1           1     1     1     1     1 1 1         1                                     1     1     1                   1                                                       1                 1                             1 1           1     1     1                 1                                                                                 1       1               1                   1                   1                 1                 1                   1                         1                                                                             1       1 15           1                 1                           1                                                                                 1         1 7         1                                                                                                           1                                                                     1   1 6                                               1                                       1                                         1                                                                                           1                             1                                     1 1                                                     1                   1                             1                                     1                           1                                         1                                                                                                                                                                         1                                                                                                                           1           1                   1                 1               1                                                                 1                                         1                                       1                       1                                     1           1     1 1                   1                                                 1                                                           1                             1                               1                             1         1 1 1 1     1     1                 1         1 1 1 1     1 1 1 1 1 1 1 1     1 1 1 1 1     1                 1                     1                                                     1         1                                               1                               1 1 1 1   1                                                                                                                                                                                       1                                                       1                       1 1   1       1 2             1   1 1             1           1         1         1                 1   1               1               1       1       1       1       1                     1                                                           1                                                                                                                                                                                                                                                                                                                                                                                       1                                         1                                                                                   1                                                                                                     1                                                                                                                 1       1                                                                         1   1 1                                             1                                                                                       1             1 6                                                   1                                                                             1                                               1                                           1   1 2                                                                                           1                                           1                     1                                                             1                                                             1                                                                                   1         1         1                 1                                                                                                                             1                                               1                                             1                                                                         1                                     1                                                               1                                           1                                         1                 1           1                                 1                                               1                                                               1                                         1                                     1                                                             1       1                                                   1                                                                                                                                   1                                         1                                                         1                                             1                                                                                                                       1                                         1   1                                                                                 1                                                                                                                         1   1 1             1   1                                                                                                                                                                           1                                             1                                                                                                                                                                                                           1                                                           1                                                                                                                                                                         1                 1                                               1                                                                                       1                                                 1                                                                     1                                                                                                                                         1                                                               1                                     1                                         1                                                                                                                                                                                   1                 1                                                         1                                                                                 1             1                                                                                                                                               1                                                                   1                                             1                                           1                                                                                                 1                       1                                                                                               1       1             1                                           1 1                         1                                                       1                                                                         1                               1                                                                                       1                                                         1                                                                               1                                                                   1                                                                                                                             1           1                                                                                                                                                                         1                                                                                                                                                                                             1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1   1        
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (factory((global.async = global.async || {})));
}(this, (function (exports) { 'use strict';
 
/**
 * A faster alternative to `Function#apply`, this function invokes `func`
 * with the `this` binding of `thisArg` and the arguments of `args`.
 *
 * @private
 * @param {Function} func The function to invoke.
 * @param {*} thisArg The `this` binding of `func`.
 * @param {Array} args The arguments to invoke `func` with.
 * @returns {*} Returns the result of `func`.
 */
function apply(func, thisArg, args) {
  switch (args.length) {
    case 0: return func.call(thisArg);
    case 1: return func.call(thisArg, args[0]);
    case 2: return func.call(thisArg, args[0], args[1]);
    case 3: return func.call(thisArg, args[0], args[1], args[2]);
  }
  return func.apply(thisArg, args);
}
 
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;
 
/**
 * A specialized version of `baseRest` which transforms the rest array.
 *
 * @private
 * @param {Function} func The function to apply a rest parameter to.
 * @param {number} [start=func.length-1] The start position of the rest parameter.
 * @param {Function} transform The rest array transform.
 * @returns {Function} Returns the new function.
 */
function overRest$1(func, start, transform) {
  start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
  return function() {
    var args = arguments,
        index = -1,
        length = nativeMax(args.length - start, 0),
        array = Array(length);
 
    while (++index < length) {
      array[index] = args[start + index];
    }
    index = -1;
    var otherArgs = Array(start + 1);
    while (++index < start) {
      otherArgs[index] = args[index];
    }
    otherArgs[start] = transform(array);
    return apply(func, this, otherArgs);
  };
}
 
/**
 * This method returns the first argument it receives.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Util
 * @param {*} value Any value.
 * @returns {*} Returns `value`.
 * @example
 *
 * var object = { 'a': 1 };
 *
 * console.log(_.identity(object) === object);
 * // => true
 */
function identity(value) {
  return value;
}
 
// Lodash rest function without function.toString()
// remappings
function rest(func, start) {
    return overRest$1(func, start, identity);
}
 
var initialParams = function (fn) {
    return rest(function (args /*..., callback*/) {
        var callback = args.pop();
        fn.call(this, args, callback);
    });
};
 
/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject([1, 2, 3]);
 * // => true
 *
 * _.isObject(_.noop);
 * // => true
 *
 * _.isObject(null);
 * // => false
 */
function isObject(value) {
  var type = typeof value;
  return value != null && (type == 'object' || type == 'function');
}
 
/**
 * Take a sync function and make it async, passing its return value to a
 * callback. This is useful for plugging sync functions into a waterfall,
 * series, or other async functions. Any arguments passed to the generated
 * function will be passed to the wrapped function (except for the final
 * callback argument). Errors thrown will be passed to the callback.
 *
 * If the function passed to `asyncify` returns a Promise, that promises's
 * resolved/rejected state will be used to call the callback, rather than simply
 * the synchronous return value.
 *
 * This also means you can asyncify ES2017 `async` functions.
 *
 * @name asyncify
 * @static
 * @memberOf module:Utils
 * @method
 * @alias wrapSync
 * @category Util
 * @param {Function} func - The synchronous funuction, or Promise-returning
 * function to convert to an {@link AsyncFunction}.
 * @returns {AsyncFunction} An asynchronous wrapper of the `func`. To be
 * invoked with `(args..., callback)`.
 * @example
 *
 * // passing a regular synchronous function
 * async.waterfall([
 *     async.apply(fs.readFile, filename, "utf8"),
 *     async.asyncify(JSON.parse),
 *     function (data, next) {
 *         // data is the result of parsing the text.
 *         // If there was a parsing error, it would have been caught.
 *     }
 * ], callback);
 *
 * // passing a function returning a promise
 * async.waterfall([
 *     async.apply(fs.readFile, filename, "utf8"),
 *     async.asyncify(function (contents) {
 *         return db.model.create(contents);
 *     }),
 *     function (model, next) {
 *         // `model` is the instantiated model object.
 *         // If there was an error, this function would be skipped.
 *     }
 * ], callback);
 *
 * // es2017 example, though `asyncify` is not needed if your JS environment
 * // supports async functions out of the box
 * var q = async.queue(async.asyncify(async function(file) {
 *     var intermediateStep = await processFile(file);
 *     return await somePromise(intermediateStep)
 * }));
 *
 * q.push(files);
 */
function asyncify(func) {
    return initialParams(function (args, callback) {
        var result;
        try {
            result = func.apply(this, args);
        } catch (e) {
            return callback(e);
        }
        // if result is Promise object
        if (isObject(result) && typeof result.then === 'function') {
            result.then(function (value) {
                callback(null, value);
            }, function (err) {
                callback(err.message ? err : new Error(err));
            });
        } else {
            callback(null, result);
        }
    });
}
 
var supportsSymbol = typeof Symbol === 'function';
 
function supportsAsync() {
    var supported;
    try {
        /* eslint no-eval: 0 */
        supported = isAsync(eval('(async function () {})'));
    } catch (e) {
        supported = false;
    }
    return supported;
}
 
function isAsync(fn) {
    return supportsSymbol && fn[Symbol.toStringTag] === 'AsyncFunction';
}
 
function wrapAsync(asyncFn) {
    return isAsync(asyncFn) ? asyncify(asyncFn) : asyncFn;
}
 
var wrapAsync$1 = supportsAsync() ? wrapAsync : identity;
 
function applyEach$1(eachfn) {
    return rest(function (fns, args) {
        var go = initialParams(function (args, callback) {
            var that = this;
            return eachfn(fns, function (fn, cb) {
                wrapAsync$1(fn).apply(that, args.concat(cb));
            }, callback);
        });
        if (args.length) {
            return go.apply(this, args);
        } else {
            return go;
        }
    });
}
 
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
 
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
 
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
 
/** Built-in value references. */
var Symbol$1 = root.Symbol;
 
/** Used for built-in method references. */
var objectProto = Object.prototype;
 
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
 
/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.toString;
 
/** Built-in value references. */
var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
 
/**
 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the raw `toStringTag`.
 */
function getRawTag(value) {
  var isOwn = hasOwnProperty.call(value, symToStringTag$1),
      tag = value[symToStringTag$1];
 
  try {
    value[symToStringTag$1] = undefined;
    var unmasked = true;
  } catch (e) {}
 
  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag$1] = tag;
    } else {
      delete value[symToStringTag$1];
    }
  }
  return result;
}
 
/** Used for built-in method references. */
var objectProto$1 = Object.prototype;
 
/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString$1 = objectProto$1.toString;
 
/**
 * Converts `value` to a string using `Object.prototype.toString`.
 *
 * @private
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 */
function objectToString(value) {
  return nativeObjectToString$1.call(value);
}
 
/** `Object#toString` result references. */
var nullTag = '[object Null]';
var undefinedTag = '[object Undefined]';
 
/** Built-in value references. */
var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
 
/**
 * The base implementation of `getTag` without fallbacks for buggy environments.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  Iif (value == null) {
    return value === undefined ? undefinedTag : nullTag;
  }
  value = Object(value);
  return (symToStringTag && symToStringTag in value)
    ? getRawTag(value)
    : objectToString(value);
}
 
/** `Object#toString` result references. */
var asyncTag = '[object AsyncFunction]';
var funcTag = '[object Function]';
var genTag = '[object GeneratorFunction]';
var proxyTag = '[object Proxy]';
 
/**
 * Checks if `value` is classified as a `Function` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
 * @example
 *
 * _.isFunction(_);
 * // => true
 *
 * _.isFunction(/abc/);
 * // => false
 */
function isFunction(value) {
  if (!isObject(value)) {
    return false;
  }
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 9 which returns 'object' for typed arrays and other constructors.
  var tag = baseGetTag(value);
  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}
 
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;
 
/**
 * Checks if `value` is a valid array-like length.
 *
 * **Note:** This method is loosely based on
 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
 * @example
 *
 * _.isLength(3);
 * // => true
 *
 * _.isLength(Number.MIN_VALUE);
 * // => false
 *
 * _.isLength(Infinity);
 * // => false
 *
 * _.isLength('3');
 * // => false
 */
function isLength(value) {
  return typeof value == 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}
 
/**
 * Checks if `value` is array-like. A value is considered array-like if it's
 * not a function and has a `value.length` that's an integer greater than or
 * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
 * @example
 *
 * _.isArrayLike([1, 2, 3]);
 * // => true
 *
 * _.isArrayLike(document.body.children);
 * // => true
 *
 * _.isArrayLike('abc');
 * // => true
 *
 * _.isArrayLike(_.noop);
 * // => false
 */
function isArrayLike(value) {
  return value != null && isLength(value.length) && !isFunction(value);
}
 
// A temporary value used to identify if the loop should be broken.
// See #1064, #1293
var breakLoop = {};
 
/**
 * This method returns `undefined`.
 *
 * @static
 * @memberOf _
 * @since 2.3.0
 * @category Util
 * @example
 *
 * _.times(2, _.noop);
 * // => [undefined, undefined]
 */
function noop() {
  // No operation performed.
}
 
function once(fn) {
    return function () {
        if (fn === null) return;
        var callFn = fn;
        fn = null;
        callFn.apply(this, arguments);
    };
}
 
var iteratorSymbol = typeof Symbol === 'function' && Symbol.iterator;
 
var getIterator = function (coll) {
    return iteratorSymbol && coll[iteratorSymbol] && coll[iteratorSymbol]();
};
 
/**
 * The base implementation of `_.times` without support for iteratee shorthands
 * or max array length checks.
 *
 * @private
 * @param {number} n The number of times to invoke `iteratee`.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the array of results.
 */
function baseTimes(n, iteratee) {
  var index = -1,
      result = Array(n);
 
  while (++index < n) {
    result[index] = iteratee(index);
  }
  return result;
}
 
/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * _.isObjectLike({});
 * // => true
 *
 * _.isObjectLike([1, 2, 3]);
 * // => true
 *
 * _.isObjectLike(_.noop);
 * // => false
 *
 * _.isObjectLike(null);
 * // => false
 */
function isObjectLike(value) {
  return value != null && typeof value == 'object';
}
 
/** `Object#toString` result references. */
var argsTag = '[object Arguments]';
 
/**
 * The base implementation of `_.isArguments`.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 */
function baseIsArguments(value) {
  return isObjectLike(value) && baseGetTag(value) == argsTag;
}
 
/** Used for built-in method references. */
var objectProto$3 = Object.prototype;
 
/** Used to check objects for own properties. */
var hasOwnProperty$2 = objectProto$3.hasOwnProperty;
 
/** Built-in value references. */
var propertyIsEnumerable = objectProto$3.propertyIsEnumerable;
 
/**
 * Checks if `value` is likely an `arguments` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 *  else `false`.
 * @example
 *
 * _.isArguments(function() { return arguments; }());
 * // => true
 *
 * _.isArguments([1, 2, 3]);
 * // => false
 */
var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
  return isObjectLike(value) && hasOwnProperty$2.call(value, 'callee') &&
    !propertyIsEnumerable.call(value, 'callee');
};
 
/**
 * Checks if `value` is classified as an `Array` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(document.body.children);
 * // => false
 *
 * _.isArray('abc');
 * // => false
 *
 * _.isArray(_.noop);
 * // => false
 */
var isArray = Array.isArray;
 
/**
 * This method returns `false`.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {boolean} Returns `false`.
 * @example
 *
 * _.times(2, _.stubFalse);
 * // => [false, false]
 */
function stubFalse() {
  return false;
}
 
/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
 
/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
 
/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;
 
/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined;
 
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
 
/**
 * Checks if `value` is a buffer.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
 * @example
 *
 * _.isBuffer(new Buffer(2));
 * // => true
 *
 * _.isBuffer(new Uint8Array(2));
 * // => false
 */
var isBuffer = nativeIsBuffer || stubFalse;
 
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER$1 = 9007199254740991;
 
/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;
 
/**
 * Checks if `value` is a valid array-like index.
 *
 * @private
 * @param {*} value The value to check.
 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
 */
function isIndex(value, length) {
  length = length == null ? MAX_SAFE_INTEGER$1 : length;
  return !!length &&
    (typeof value == 'number' || reIsUint.test(value)) &&
    (value > -1 && value % 1 == 0 && value < length);
}
 
/** `Object#toString` result references. */
var argsTag$1 = '[object Arguments]';
var arrayTag = '[object Array]';
var boolTag = '[object Boolean]';
var dateTag = '[object Date]';
var errorTag = '[object Error]';
var funcTag$1 = '[object Function]';
var mapTag = '[object Map]';
var numberTag = '[object Number]';
var objectTag = '[object Object]';
var regexpTag = '[object RegExp]';
var setTag = '[object Set]';
var stringTag = '[object String]';
var weakMapTag = '[object WeakMap]';
 
var arrayBufferTag = '[object ArrayBuffer]';
var dataViewTag = '[object DataView]';
var float32Tag = '[object Float32Array]';
var float64Tag = '[object Float64Array]';
var int8Tag = '[object Int8Array]';
var int16Tag = '[object Int16Array]';
var int32Tag = '[object Int32Array]';
var uint8Tag = '[object Uint8Array]';
var uint8ClampedTag = '[object Uint8ClampedArray]';
var uint16Tag = '[object Uint16Array]';
var uint32Tag = '[object Uint32Array]';
 
/** Used to identify `toStringTag` values of typed arrays. */
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
typedArrayTags[uint32Tag] = true;
typedArrayTags[argsTag$1] = typedArrayTags[arrayTag] =
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
typedArrayTags[errorTag] = typedArrayTags[funcTag$1] =
typedArrayTags[mapTag] = typedArrayTags[numberTag] =
typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
typedArrayTags[setTag] = typedArrayTags[stringTag] =
typedArrayTags[weakMapTag] = false;
 
/**
 * The base implementation of `_.isTypedArray` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 */
function baseIsTypedArray(value) {
  return isObjectLike(value) &&
    isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
}
 
/**
 * The base implementation of `_.unary` without support for storing metadata.
 *
 * @private
 * @param {Function} func The function to cap arguments for.
 * @returns {Function} Returns the new capped function.
 */
function baseUnary(func) {
  return function(value) {
    return func(value);
  };
}
 
/** Detect free variable `exports`. */
var freeExports$1 = typeof exports == 'object' && exports && !exports.nodeType && exports;
 
/** Detect free variable `module`. */
var freeModule$1 = freeExports$1 && typeof module == 'object' && module && !module.nodeType && module;
 
/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports$1 = freeModule$1 && freeModule$1.exports === freeExports$1;
 
/** Detect free variable `process` from Node.js. */
var freeProcess = moduleExports$1 && freeGlobal.process;
 
/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
  try {
    return freeProcess && freeProcess.binding('util');
  } catch (e) {}
}());
 
/* Node.js helper references. */
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
 
/**
 * Checks if `value` is classified as a typed array.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 * @example
 *
 * _.isTypedArray(new Uint8Array);
 * // => true
 *
 * _.isTypedArray([]);
 * // => false
 */
var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
 
/** Used for built-in method references. */
var objectProto$2 = Object.prototype;
 
/** Used to check objects for own properties. */
var hasOwnProperty$1 = objectProto$2.hasOwnProperty;
 
/**
 * Creates an array of the enumerable property names of the array-like `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @param {boolean} inherited Specify returning inherited property names.
 * @returns {Array} Returns the array of property names.
 */
function arrayLikeKeys(value, inherited) {
  var isArr = isArray(value),
      isArg = !isArr && isArguments(value),
      isBuff = !isArr && !isArg && isBuffer(value),
      isType = !isArr && !isArg && !isBuff && isTypedArray(value),
      skipIndexes = isArr || isArg || isBuff || isType,
      result = skipIndexes ? baseTimes(value.length, String) : [],
      length = result.length;
 
  for (var key in value) {
    if ((inherited || hasOwnProperty$1.call(value, key)) &&
        !(skipIndexes && (
           // Safari 9 has enumerable `arguments.length` in strict mode.
           key == 'length' ||
           // Node.js 0.10 has enumerable non-index properties on buffers.
           (isBuff && (key == 'offset' || key == 'parent')) ||
           // PhantomJS 2 has enumerable non-index properties on typed arrays.
           (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
           // Skip index properties.
           isIndex(key, length)
        ))) {
      result.push(key);
    }
  }
  return result;
}
 
/** Used for built-in method references. */
var objectProto$5 = Object.prototype;
 
/**
 * Checks if `value` is likely a prototype object.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
 */
function isPrototype(value) {
  var Ctor = value && value.constructor,
      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto$5;
 
  return value === proto;
}
 
/**
 * Creates a unary function that invokes `func` with its argument transformed.
 *
 * @private
 * @param {Function} func The function to wrap.
 * @param {Function} transform The argument transform.
 * @returns {Function} Returns the new function.
 */
function overArg(func, transform) {
  return function(arg) {
    return func(transform(arg));
  };
}
 
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeKeys = overArg(Object.keys, Object);
 
/** Used for built-in method references. */
var objectProto$4 = Object.prototype;
 
/** Used to check objects for own properties. */
var hasOwnProperty$3 = objectProto$4.hasOwnProperty;
 
/**
 * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function baseKeys(object) {
  if (!isPrototype(object)) {
    return nativeKeys(object);
  }
  var result = [];
  for (var key in Object(object)) {
    if (hasOwnProperty$3.call(object, key) && key != 'constructor') {
      result.push(key);
    }
  }
  return result;
}
 
/**
 * Creates an array of the own enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects. See the
 * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * for more details.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keys(new Foo);
 * // => ['a', 'b'] (iteration order is not guaranteed)
 *
 * _.keys('hi');
 * // => ['0', '1']
 */
function keys(object) {
  return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
 
function createArrayIterator(coll) {
    var i = -1;
    var len = coll.length;
    return function next() {
        return ++i < len ? { value: coll[i], key: i } : null;
    };
}
 
function createES2015Iterator(iterator) {
    var i = -1;
    return function next() {
        var item = iterator.next();
        if (item.done) return null;
        i++;
        return { value: item.value, key: i };
    };
}
 
function createObjectIterator(obj) {
    var okeys = keys(obj);
    var i = -1;
    var len = okeys.length;
    return function next() {
        var key = okeys[++i];
        return i < len ? { value: obj[key], key: key } : null;
    };
}
 
function iterator(coll) {
    if (isArrayLike(coll)) {
        return createArrayIterator(coll);
    }
 
    var iterator = getIterator(coll);
    return iterator ? createES2015Iterator(iterator) : createObjectIterator(coll);
}
 
function onlyOnce(fn) {
    return function () {
        if (fn === null) throw new Error("Callback was already called.");
        var callFn = fn;
        fn = null;
        callFn.apply(this, arguments);
    };
}
 
function _eachOfLimit(limit) {
    return function (obj, iteratee, callback) {
        callback = once(callback || noop);
        if (limit <= 0 || !obj) {
            return callback(null);
        }
        var nextElem = iterator(obj);
        var done = false;
        var running = 0;
 
        function iterateeCallback(err, value) {
            running -= 1;
            if (err) {
                done = true;
                callback(err);
            } else if (value === breakLoop || done && running <= 0) {
                done = true;
                return callback(null);
            } else {
                replenish();
            }
        }
 
        function replenish() {
            while (running < limit && !done) {
                var elem = nextElem();
                if (elem === null) {
                    done = true;
                    if (running <= 0) {
                        callback(null);
                    }
                    return;
                }
                running += 1;
                iteratee(elem.value, elem.key, onlyOnce(iterateeCallback));
            }
        }
 
        replenish();
    };
}
 
/**
 * The same as [`eachOf`]{@link module:Collections.eachOf} but runs a maximum of `limit` async operations at a
 * time.
 *
 * @name eachOfLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.eachOf]{@link module:Collections.eachOf}
 * @alias forEachOfLimit
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - An async function to apply to each
 * item in `coll`. The `key` is the item's key, or index in the case of an
 * array.
 * Invoked with (item, key, callback).
 * @param {Function} [callback] - A callback which is called when all
 * `iteratee` functions have finished, or an error occurs. Invoked with (err).
 */
function eachOfLimit(coll, limit, iteratee, callback) {
  _eachOfLimit(limit)(coll, wrapAsync$1(iteratee), callback);
}
 
function doLimit(fn, limit) {
    return function (iterable, iteratee, callback) {
        return fn(iterable, limit, iteratee, callback);
    };
}
 
// eachOf implementation optimized for array-likes
function eachOfArrayLike(coll, iteratee, callback) {
    callback = once(callback || noop);
    var index = 0,
        completed = 0,
        length = coll.length;
    if (length === 0) {
        callback(null);
    }
 
    function iteratorCallback(err, value) {
        if (err) {
            callback(err);
        } else if (++completed === length || value === breakLoop) {
            callback(null);
        }
    }
 
    for (; index < length; index++) {
        iteratee(coll[index], index, onlyOnce(iteratorCallback));
    }
}
 
// a generic version of eachOf which can handle array, object, and iterator cases.
var eachOfGeneric = doLimit(eachOfLimit, Infinity);
 
/**
 * Like [`each`]{@link module:Collections.each}, except that it passes the key (or index) as the second argument
 * to the iteratee.
 *
 * @name eachOf
 * @static
 * @memberOf module:Collections
 * @method
 * @alias forEachOf
 * @category Collection
 * @see [async.each]{@link module:Collections.each}
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - A function to apply to each
 * item in `coll`.
 * The `key` is the item's key, or index in the case of an array.
 * Invoked with (item, key, callback).
 * @param {Function} [callback] - A callback which is called when all
 * `iteratee` functions have finished, or an error occurs. Invoked with (err).
 * @example
 *
 * var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"};
 * var configs = {};
 *
 * async.forEachOf(obj, function (value, key, callback) {
 *     fs.readFile(__dirname + value, "utf8", function (err, data) {
 *         if (err) return callback(err);
 *         try {
 *             configs[key] = JSON.parse(data);
 *         } catch (e) {
 *             return callback(e);
 *         }
 *         callback();
 *     });
 * }, function (err) {
 *     if (err) console.error(err.message);
 *     // configs is now a map of JSON data
 *     doSomethingWith(configs);
 * });
 */
var eachOf = function (coll, iteratee, callback) {
    var eachOfImplementation = isArrayLike(coll) ? eachOfArrayLike : eachOfGeneric;
    eachOfImplementation(coll, wrapAsync$1(iteratee), callback);
};
 
function doParallel(fn) {
    return function (obj, iteratee, callback) {
        return fn(eachOf, obj, wrapAsync$1(iteratee), callback);
    };
}
 
function _asyncMap(eachfn, arr, iteratee, callback) {
    callback = callback || noop;
    arr = arr || [];
    var results = [];
    var counter = 0;
    var _iteratee = wrapAsync$1(iteratee);
 
    eachfn(arr, function (value, _, callback) {
        var index = counter++;
        _iteratee(value, function (err, v) {
            results[index] = v;
            callback(err);
        });
    }, function (err) {
        callback(err, results);
    });
}
 
/**
 * Produces a new collection of values by mapping each value in `coll` through
 * the `iteratee` function. The `iteratee` is called with an item from `coll`
 * and a callback for when it has finished processing. Each of these callback
 * takes 2 arguments: an `error`, and the transformed item from `coll`. If
 * `iteratee` passes an error to its callback, the main `callback` (for the
 * `map` function) is immediately called with the error.
 *
 * Note, that since this function applies the `iteratee` to each item in
 * parallel, there is no guarantee that the `iteratee` functions will complete
 * in order. However, the results array will be in the same order as the
 * original `coll`.
 *
 * If `map` is passed an Object, the results will be an Array.  The results
 * will roughly be in the order of the original Objects' keys (but this can
 * vary across JavaScript engines).
 *
 * @name map
 * @static
 * @memberOf module:Collections
 * @method
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The iteratee should complete with the transformed item.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. Results is an Array of the
 * transformed items from the `coll`. Invoked with (err, results).
 * @example
 *
 * async.map(['file1','file2','file3'], fs.stat, function(err, results) {
 *     // results is now an array of stats for each file
 * });
 */
var map = doParallel(_asyncMap);
 
/**
 * Applies the provided arguments to each function in the array, calling
 * `callback` after all functions have completed. If you only provide the first
 * argument, `fns`, then it will return a function which lets you pass in the
 * arguments as if it were a single function call. If more arguments are
 * provided, `callback` is required while `args` is still optional.
 *
 * @name applyEach
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {Array|Iterable|Object} fns - A collection of {@link AsyncFunction}s
 * to all call with the same arguments
 * @param {...*} [args] - any number of separate arguments to pass to the
 * function.
 * @param {Function} [callback] - the final argument should be the callback,
 * called when all functions have completed processing.
 * @returns {Function} - If only the first argument, `fns`, is provided, it will
 * return a function which lets you pass in the arguments as if it were a single
 * function call. The signature is `(..args, callback)`. If invoked with any
 * arguments, `callback` is required.
 * @example
 *
 * async.applyEach([enableSearch, updateSchema], 'bucket', callback);
 *
 * // partial application example:
 * async.each(
 *     buckets,
 *     async.applyEach([enableSearch, updateSchema]),
 *     callback
 * );
 */
var applyEach = applyEach$1(map);
 
function doParallelLimit(fn) {
    return function (obj, limit, iteratee, callback) {
        return fn(_eachOfLimit(limit), obj, wrapAsync$1(iteratee), callback);
    };
}
 
/**
 * The same as [`map`]{@link module:Collections.map} but runs a maximum of `limit` async operations at a time.
 *
 * @name mapLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.map]{@link module:Collections.map}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The iteratee should complete with the transformed item.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. Results is an array of the
 * transformed items from the `coll`. Invoked with (err, results).
 */
var mapLimit = doParallelLimit(_asyncMap);
 
/**
 * The same as [`map`]{@link module:Collections.map} but runs only a single async operation at a time.
 *
 * @name mapSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.map]{@link module:Collections.map}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The iteratee should complete with the transformed item.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. Results is an array of the
 * transformed items from the `coll`. Invoked with (err, results).
 */
var mapSeries = doLimit(mapLimit, 1);
 
/**
 * The same as [`applyEach`]{@link module:ControlFlow.applyEach} but runs only a single async operation at a time.
 *
 * @name applyEachSeries
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.applyEach]{@link module:ControlFlow.applyEach}
 * @category Control Flow
 * @param {Array|Iterable|Object} fns - A collection of {@link AsyncFunction}s to all
 * call with the same arguments
 * @param {...*} [args] - any number of separate arguments to pass to the
 * function.
 * @param {Function} [callback] - the final argument should be the callback,
 * called when all functions have completed processing.
 * @returns {Function} - If only the first argument is provided, it will return
 * a function which lets you pass in the arguments as if it were a single
 * function call.
 */
var applyEachSeries = applyEach$1(mapSeries);
 
/**
 * Creates a continuation function with some arguments already applied.
 *
 * Useful as a shorthand when combined with other control flow functions. Any
 * arguments passed to the returned function are added to the arguments
 * originally passed to apply.
 *
 * @name apply
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {Function} function - The function you want to eventually apply all
 * arguments to. Invokes with (arguments...).
 * @param {...*} arguments... - Any number of arguments to automatically apply
 * when the continuation is called.
 * @example
 *
 * // using apply
 * async.parallel([
 *     async.apply(fs.writeFile, 'testfile1', 'test1'),
 *     async.apply(fs.writeFile, 'testfile2', 'test2')
 * ]);
 *
 *
 * // the same process without using apply
 * async.parallel([
 *     function(callback) {
 *         fs.writeFile('testfile1', 'test1', callback);
 *     },
 *     function(callback) {
 *         fs.writeFile('testfile2', 'test2', callback);
 *     }
 * ]);
 *
 * // It's possible to pass any number of additional arguments when calling the
 * // continuation:
 *
 * node> var fn = async.apply(sys.puts, 'one');
 * node> fn('two', 'three');
 * one
 * two
 * three
 */
var apply$2 = rest(function (fn, args) {
    return rest(function (callArgs) {
        return fn.apply(null, args.concat(callArgs));
    });
});
 
/**
 * A specialized version of `_.forEach` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns `array`.
 */
function arrayEach(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length;
 
  while (++index < length) {
    if (iteratee(array[index], index, array) === false) {
      break;
    }
  }
  return array;
}
 
/**
 * Creates a base function for methods like `_.forIn` and `_.forOwn`.
 *
 * @private
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {Function} Returns the new base function.
 */
function createBaseFor(fromRight) {
  return function(object, iteratee, keysFunc) {
    var index = -1,
        iterable = Object(object),
        props = keysFunc(object),
        length = props.length;
 
    while (length--) {
      var key = props[fromRight ? length : ++index];
      if (iteratee(iterable[key], key, iterable) === false) {
        break;
      }
    }
    return object;
  };
}
 
/**
 * The base implementation of `baseForOwn` which iterates over `object`
 * properties returned by `keysFunc` and invokes `iteratee` for each property.
 * Iteratee functions may exit iteration early by explicitly returning `false`.
 *
 * @private
 * @param {Object} object The object to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @returns {Object} Returns `object`.
 */
var baseFor = createBaseFor();
 
/**
 * The base implementation of `_.forOwn` without support for iteratee shorthands.
 *
 * @private
 * @param {Object} object The object to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Object} Returns `object`.
 */
function baseForOwn(object, iteratee) {
  return object && baseFor(object, iteratee, keys);
}
 
/**
 * The base implementation of `_.findIndex` and `_.findLastIndex` without
 * support for iteratee shorthands.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {Function} predicate The function invoked per iteration.
 * @param {number} fromIndex The index to search from.
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  var length = array.length,
      index = fromIndex + (fromRight ? 1 : -1);
 
  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index;
    }
  }
  return -1;
}
 
/**
 * The base implementation of `_.isNaN` without support for number objects.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
 */
function baseIsNaN(value) {
  return value !== value;
}
 
/**
 * A specialized version of `_.indexOf` which performs strict equality
 * comparisons of values, i.e. `===`.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function strictIndexOf(array, value, fromIndex) {
  var index = fromIndex - 1,
      length = array.length;
 
  while (++index < length) {
    if (array[index] === value) {
      return index;
    }
  }
  return -1;
}
 
/**
 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseIndexOf(array, value, fromIndex) {
  return value === value
    ? strictIndexOf(array, value, fromIndex)
    : baseFindIndex(array, baseIsNaN, fromIndex);
}
 
/**
 * Determines the best order for running the {@link AsyncFunction}s in `tasks`, based on
 * their requirements. Each function can optionally depend on other functions
 * being completed first, and each function is run as soon as its requirements
 * are satisfied.
 *
 * If any of the {@link AsyncFunction}s pass an error to their callback, the `auto` sequence
 * will stop. Further tasks will not execute (so any other functions depending
 * on it will not run), and the main `callback` is immediately called with the
 * error.
 *
 * {@link AsyncFunction}s also receive an object containing the results of functions which
 * have completed so far as the first argument, if they have dependencies. If a
 * task function has no dependencies, it will only be passed a callback.
 *
 * @name auto
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {Object} tasks - An object. Each of its properties is either a
 * function or an array of requirements, with the {@link AsyncFunction} itself the last item
 * in the array. The object's key of a property serves as the name of the task
 * defined by that property, i.e. can be used when specifying requirements for
 * other tasks. The function receives one or two arguments:
 * * a `results` object, containing the results of the previously executed
 *   functions, only passed if the task has any dependencies,
 * * a `callback(err, result)` function, which must be called when finished,
 *   passing an `error` (which can be `null`) and the result of the function's
 *   execution.
 * @param {number} [concurrency=Infinity] - An optional `integer` for
 * determining the maximum number of tasks that can be run in parallel. By
 * default, as many as possible.
 * @param {Function} [callback] - An optional callback which is called when all
 * the tasks have been completed. It receives the `err` argument if any `tasks`
 * pass an error to their callback. Results are always returned; however, if an
 * error occurs, no further `tasks` will be performed, and the results object
 * will only contain partial results. Invoked with (err, results).
 * @returns undefined
 * @example
 *
 * async.auto({
 *     // this function will just be passed a callback
 *     readData: async.apply(fs.readFile, 'data.txt', 'utf-8'),
 *     showData: ['readData', function(results, cb) {
 *         // results.readData is the file's contents
 *         // ...
 *     }]
 * }, callback);
 *
 * async.auto({
 *     get_data: function(callback) {
 *         console.log('in get_data');
 *         // async code to get some data
 *         callback(null, 'data', 'converted to array');
 *     },
 *     make_folder: function(callback) {
 *         console.log('in make_folder');
 *         // async code to create a directory to store a file in
 *         // this is run at the same time as getting the data
 *         callback(null, 'folder');
 *     },
 *     write_file: ['get_data', 'make_folder', function(results, callback) {
 *         console.log('in write_file', JSON.stringify(results));
 *         // once there is some data and the directory exists,
 *         // write the data to a file in the directory
 *         callback(null, 'filename');
 *     }],
 *     email_link: ['write_file', function(results, callback) {
 *         console.log('in email_link', JSON.stringify(results));
 *         // once the file is written let's email a link to it...
 *         // results.write_file contains the filename returned by write_file.
 *         callback(null, {'file':results.write_file, 'email':'user@example.com'});
 *     }]
 * }, function(err, results) {
 *     console.log('err = ', err);
 *     console.log('results = ', results);
 * });
 */
var auto = function (tasks, concurrency, callback) {
    if (typeof concurrency === 'function') {
        // concurrency is optional, shift the args.
        callback = concurrency;
        concurrency = null;
    }
    callback = once(callback || noop);
    var keys$$1 = keys(tasks);
    var numTasks = keys$$1.length;
    if (!numTasks) {
        return callback(null);
    }
    if (!concurrency) {
        concurrency = numTasks;
    }
 
    var results = {};
    var runningTasks = 0;
    var hasError = false;
 
    var listeners = Object.create(null);
 
    var readyTasks = [];
 
    // for cycle detection:
    var readyToCheck = []; // tasks that have been identified as reachable
    // without the possibility of returning to an ancestor task
    var uncheckedDependencies = {};
 
    baseForOwn(tasks, function (task, key) {
        if (!isArray(task)) {
            // no dependencies
            enqueueTask(key, [task]);
            readyToCheck.push(key);
            return;
        }
 
        var dependencies = task.slice(0, task.length - 1);
        var remainingDependencies = dependencies.length;
        if (remainingDependencies === 0) {
            enqueueTask(key, task);
            readyToCheck.push(key);
            return;
        }
        uncheckedDependencies[key] = remainingDependencies;
 
        arrayEach(dependencies, function (dependencyName) {
            if (!tasks[dependencyName]) {
                throw new Error('async.auto task `' + key + '` has a non-existent dependency `' + dependencyName + '` in ' + dependencies.join(', '));
            }
            addListener(dependencyName, function () {
                remainingDependencies--;
                if (remainingDependencies === 0) {
                    enqueueTask(key, task);
                }
            });
        });
    });
 
    checkForDeadlocks();
    processQueue();
 
    function enqueueTask(key, task) {
        readyTasks.push(function () {
            runTask(key, task);
        });
    }
 
    function processQueue() {
        if (readyTasks.length === 0 && runningTasks === 0) {
            return callback(null, results);
        }
        while (readyTasks.length && runningTasks < concurrency) {
            var run = readyTasks.shift();
            run();
        }
    }
 
    function addListener(taskName, fn) {
        var taskListeners = listeners[taskName];
        if (!taskListeners) {
            taskListeners = listeners[taskName] = [];
        }
 
        taskListeners.push(fn);
    }
 
    function taskComplete(taskName) {
        var taskListeners = listeners[taskName] || [];
        arrayEach(taskListeners, function (fn) {
            fn();
        });
        processQueue();
    }
 
    function runTask(key, task) {
        if (hasError) return;
 
        var taskCallback = onlyOnce(rest(function (err, args) {
            runningTasks--;
            if (args.length <= 1) {
                args = args[0];
            }
            if (err) {
                var safeResults = {};
                baseForOwn(results, function (val, rkey) {
                    safeResults[rkey] = val;
                });
                safeResults[key] = args;
                hasError = true;
                listeners = Object.create(null);
 
                callback(err, safeResults);
            } else {
                results[key] = args;
                taskComplete(key);
            }
        }));
 
        runningTasks++;
        var taskFn = wrapAsync$1(task[task.length - 1]);
        if (task.length > 1) {
            taskFn(results, taskCallback);
        } else {
            taskFn(taskCallback);
        }
    }
 
    function checkForDeadlocks() {
        // Kahn's algorithm
        // https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm
        // http://connalle.blogspot.com/2013/10/topological-sortingkahn-algorithm.html
        var currentTask;
        var counter = 0;
        while (readyToCheck.length) {
            currentTask = readyToCheck.pop();
            counter++;
            arrayEach(getDependents(currentTask), function (dependent) {
                if (--uncheckedDependencies[dependent] === 0) {
                    readyToCheck.push(dependent);
                }
            });
        }
 
        if (counter !== numTasks) {
            throw new Error('async.auto cannot execute tasks due to a recursive dependency');
        }
    }
 
    function getDependents(taskName) {
        var result = [];
        baseForOwn(tasks, function (task, key) {
            if (isArray(task) && baseIndexOf(task, taskName, 0) >= 0) {
                result.push(key);
            }
        });
        return result;
    }
};
 
/**
 * A specialized version of `_.map` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 */
function arrayMap(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length,
      result = Array(length);
 
  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}
 
/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';
 
/**
 * Checks if `value` is classified as a `Symbol` primitive or object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
 * @example
 *
 * _.isSymbol(Symbol.iterator);
 * // => true
 *
 * _.isSymbol('abc');
 * // => false
 */
function isSymbol(value) {
  return typeof value == 'symbol' ||
    (isObjectLike(value) && baseGetTag(value) == symbolTag);
}
 
/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;
 
/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol$1 ? Symbol$1.prototype : undefined;
var symbolToString = symbolProto ? symbolProto.toString : undefined;
 
/**
 * The base implementation of `_.toString` which doesn't convert nullish
 * values to empty strings.
 *
 * @private
 * @param {*} value The value to process.
 * @returns {string} Returns the string.
 */
function baseToString(value) {
  // Exit early for strings to avoid a performance hit in some environments.
  if (typeof value == 'string') {
    return value;
  }
  if (isArray(value)) {
    // Recursively convert values (susceptible to call stack limits).
    return arrayMap(value, baseToString) + '';
  }
  if (isSymbol(value)) {
    return symbolToString ? symbolToString.call(value) : '';
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}
 
/**
 * The base implementation of `_.slice` without an iteratee call guard.
 *
 * @private
 * @param {Array} array The array to slice.
 * @param {number} [start=0] The start position.
 * @param {number} [end=array.length] The end position.
 * @returns {Array} Returns the slice of `array`.
 */
function baseSlice(array, start, end) {
  var index = -1,
      length = array.length;
 
  if (start < 0) {
    start = -start > length ? 0 : (length + start);
  }
  end = end > length ? length : end;
  if (end < 0) {
    end += length;
  }
  length = start > end ? 0 : ((end - start) >>> 0);
  start >>>= 0;
 
  var result = Array(length);
  while (++index < length) {
    result[index] = array[index + start];
  }
  return result;
}
 
/**
 * Casts `array` to a slice if it's needed.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {number} start The start position.
 * @param {number} [end=array.length] The end position.
 * @returns {Array} Returns the cast slice.
 */
function castSlice(array, start, end) {
  var length = array.length;
  end = end === undefined ? length : end;
  return (!start && end >= length) ? array : baseSlice(array, start, end);
}
 
/**
 * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
 * that is not found in the character symbols.
 *
 * @private
 * @param {Array} strSymbols The string symbols to inspect.
 * @param {Array} chrSymbols The character symbols to find.
 * @returns {number} Returns the index of the last unmatched string symbol.
 */
function charsEndIndex(strSymbols, chrSymbols) {
  var index = strSymbols.length;
 
  while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
  return index;
}
 
/**
 * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
 * that is not found in the character symbols.
 *
 * @private
 * @param {Array} strSymbols The string symbols to inspect.
 * @param {Array} chrSymbols The character symbols to find.
 * @returns {number} Returns the index of the first unmatched string symbol.
 */
function charsStartIndex(strSymbols, chrSymbols) {
  var index = -1,
      length = strSymbols.length;
 
  while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
  return index;
}
 
/**
 * Converts an ASCII `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function asciiToArray(string) {
  return string.split('');
}
 
/** Used to compose unicode character classes. */
var rsAstralRange = '\\ud800-\\udfff';
var rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23';
var rsComboSymbolsRange = '\\u20d0-\\u20f0';
var rsVarRange = '\\ufe0e\\ufe0f';
 
/** Used to compose unicode capture groups. */
var rsZWJ = '\\u200d';
 
/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange  + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
 
/**
 * Checks if `string` contains Unicode symbols.
 *
 * @private
 * @param {string} string The string to inspect.
 * @returns {boolean} Returns `true` if a symbol is found, else `false`.
 */
function hasUnicode(string) {
  return reHasUnicode.test(string);
}
 
/** Used to compose unicode character classes. */
var rsAstralRange$1 = '\\ud800-\\udfff';
var rsComboMarksRange$1 = '\\u0300-\\u036f\\ufe20-\\ufe23';
var rsComboSymbolsRange$1 = '\\u20d0-\\u20f0';
var rsVarRange$1 = '\\ufe0e\\ufe0f';
 
/** Used to compose unicode capture groups. */
var rsAstral = '[' + rsAstralRange$1 + ']';
var rsCombo = '[' + rsComboMarksRange$1 + rsComboSymbolsRange$1 + ']';
var rsFitz = '\\ud83c[\\udffb-\\udfff]';
var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')';
var rsNonAstral = '[^' + rsAstralRange$1 + ']';
var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}';
var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]';
var rsZWJ$1 = '\\u200d';
 
/** Used to compose unicode regexes. */
var reOptMod = rsModifier + '?';
var rsOptVar = '[' + rsVarRange$1 + ']?';
var rsOptJoin = '(?:' + rsZWJ$1 + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*';
var rsSeq = rsOptVar + reOptMod + rsOptJoin;
var rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
 
/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
 
/**
 * Converts a Unicode `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function unicodeToArray(string) {
  return string.match(reUnicode) || [];
}
 
/**
 * Converts `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function stringToArray(string) {
  return hasUnicode(string)
    ? unicodeToArray(string)
    : asciiToArray(string);
}
 
/**
 * Converts `value` to a string. An empty string is returned for `null`
 * and `undefined` values. The sign of `-0` is preserved.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.toString(null);
 * // => ''
 *
 * _.toString(-0);
 * // => '-0'
 *
 * _.toString([1, 2, 3]);
 * // => '1,2,3'
 */
function toString(value) {
  return value == null ? '' : baseToString(value);
}
 
/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;
 
/**
 * Removes leading and trailing whitespace or specified characters from `string`.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category String
 * @param {string} [string=''] The string to trim.
 * @param {string} [chars=whitespace] The characters to trim.
 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
 * @returns {string} Returns the trimmed string.
 * @example
 *
 * _.trim('  abc  ');
 * // => 'abc'
 *
 * _.trim('-_-abc-_-', '_-');
 * // => 'abc'
 *
 * _.map(['  foo  ', '  bar  '], _.trim);
 * // => ['foo', 'bar']
 */
function trim(string, chars, guard) {
  string = toString(string);
  if (string && (guard || chars === undefined)) {
    return string.replace(reTrim, '');
  }
  if (!string || !(chars = baseToString(chars))) {
    return string;
  }
  var strSymbols = stringToArray(string),
      chrSymbols = stringToArray(chars),
      start = charsStartIndex(strSymbols, chrSymbols),
      end = charsEndIndex(strSymbols, chrSymbols) + 1;
 
  return castSlice(strSymbols, start, end).join('');
}
 
var FN_ARGS = /^(?:async\s+)?(function)?\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /(=.+)?(\s*)$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
 
function parseParams(func) {
    func = func.toString().replace(STRIP_COMMENTS, '');
    func = func.match(FN_ARGS)[2].replace(' ', '');
    func = func ? func.split(FN_ARG_SPLIT) : [];
    func = func.map(function (arg) {
        return trim(arg.replace(FN_ARG, ''));
    });
    return func;
}
 
/**
 * A dependency-injected version of the [async.auto]{@link module:ControlFlow.auto} function. Dependent
 * tasks are specified as parameters to the function, after the usual callback
 * parameter, with the parameter names matching the names of the tasks it
 * depends on. This can provide even more readable task graphs which can be
 * easier to maintain.
 *
 * If a final callback is specified, the task results are similarly injected,
 * specified as named parameters after the initial error parameter.
 *
 * The autoInject function is purely syntactic sugar and its semantics are
 * otherwise equivalent to [async.auto]{@link module:ControlFlow.auto}.
 *
 * @name autoInject
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.auto]{@link module:ControlFlow.auto}
 * @category Control Flow
 * @param {Object} tasks - An object, each of whose properties is an {@link AsyncFunction} of
 * the form 'func([dependencies...], callback). The object's key of a property
 * serves as the name of the task defined by that property, i.e. can be used
 * when specifying requirements for other tasks.
 * * The `callback` parameter is a `callback(err, result)` which must be called
 *   when finished, passing an `error` (which can be `null`) and the result of
 *   the function's execution. The remaining parameters name other tasks on
 *   which the task is dependent, and the results from those tasks are the
 *   arguments of those parameters.
 * @param {Function} [callback] - An optional callback which is called when all
 * the tasks have been completed. It receives the `err` argument if any `tasks`
 * pass an error to their callback, and a `results` object with any completed
 * task results, similar to `auto`.
 * @example
 *
 * //  The example from `auto` can be rewritten as follows:
 * async.autoInject({
 *     get_data: function(callback) {
 *         // async code to get some data
 *         callback(null, 'data', 'converted to array');
 *     },
 *     make_folder: function(callback) {
 *         // async code to create a directory to store a file in
 *         // this is run at the same time as getting the data
 *         callback(null, 'folder');
 *     },
 *     write_file: function(get_data, make_folder, callback) {
 *         // once there is some data and the directory exists,
 *         // write the data to a file in the directory
 *         callback(null, 'filename');
 *     },
 *     email_link: function(write_file, callback) {
 *         // once the file is written let's email a link to it...
 *         // write_file contains the filename returned by write_file.
 *         callback(null, {'file':write_file, 'email':'user@example.com'});
 *     }
 * }, function(err, results) {
 *     console.log('err = ', err);
 *     console.log('email_link = ', results.email_link);
 * });
 *
 * // If you are using a JS minifier that mangles parameter names, `autoInject`
 * // will not work with plain functions, since the parameter names will be
 * // collapsed to a single letter identifier.  To work around this, you can
 * // explicitly specify the names of the parameters your task function needs
 * // in an array, similar to Angular.js dependency injection.
 *
 * // This still has an advantage over plain `auto`, since the results a task
 * // depends on are still spread into arguments.
 * async.autoInject({
 *     //...
 *     write_file: ['get_data', 'make_folder', function(get_data, make_folder, callback) {
 *         callback(null, 'filename');
 *     }],
 *     email_link: ['write_file', function(write_file, callback) {
 *         callback(null, {'file':write_file, 'email':'user@example.com'});
 *     }]
 *     //...
 * }, function(err, results) {
 *     console.log('err = ', err);
 *     console.log('email_link = ', results.email_link);
 * });
 */
function autoInject(tasks, callback) {
    var newTasks = {};
 
    baseForOwn(tasks, function (taskFn, key) {
        var params;
        var fnIsAsync = isAsync(taskFn);
        var hasNoDeps = !fnIsAsync && taskFn.length === 1 || fnIsAsync && taskFn.length === 0;
 
        if (isArray(taskFn)) {
            params = taskFn.slice(0, -1);
            taskFn = taskFn[taskFn.length - 1];
 
            newTasks[key] = params.concat(params.length > 0 ? newTask : taskFn);
        } else if (hasNoDeps) {
            // no dependencies, use the function as-is
            newTasks[key] = taskFn;
        } else {
            params = parseParams(taskFn);
            if (taskFn.length === 0 && !fnIsAsync && params.length === 0) {
                throw new Error("autoInject task functions require explicit parameters.");
            }
 
            // remove callback param
            if (!fnIsAsync) params.pop();
 
            newTasks[key] = params.concat(newTask);
        }
 
        function newTask(results, taskCb) {
            var newArgs = arrayMap(params, function (name) {
                return results[name];
            });
            newArgs.push(taskCb);
            wrapAsync$1(taskFn).apply(null, newArgs);
        }
    });
 
    auto(newTasks, callback);
}
 
var hasSetImmediate = typeof setImmediate === 'function' && setImmediate;
var hasNextTick = typeof process === 'object' && typeof process.nextTick === 'function';
 
function fallback(fn) {
    setTimeout(fn, 0);
}
 
function wrap(defer) {
    return rest(function (fn, args) {
        defer(function () {
            fn.apply(null, args);
        });
    });
}
 
var _defer;
 
Eif (hasSetImmediate) {
    _defer = setImmediate;
} else if (hasNextTick) {
    _defer = process.nextTick;
} else {
    _defer = fallback;
}
 
var setImmediate$1 = wrap(_defer);
 
// Simple doubly linked list (https://en.wikipedia.org/wiki/Doubly_linked_list) implementation
// used for queues. This implementation assumes that the node provided by the user can be modified
// to adjust the next and last properties. We implement only the minimal functionality
// for queue support.
function DLL() {
    this.head = this.tail = null;
    this.length = 0;
}
 
function setInitial(dll, node) {
    dll.length = 1;
    dll.head = dll.tail = node;
}
 
DLL.prototype.removeLink = function (node) {
    if (node.prev) node.prev.next = node.next;else this.head = node.next;
    if (node.next) node.next.prev = node.prev;else this.tail = node.prev;
 
    node.prev = node.next = null;
    this.length -= 1;
    return node;
};
 
DLL.prototype.empty = DLL;
 
DLL.prototype.insertAfter = function (node, newNode) {
    newNode.prev = node;
    newNode.next = node.next;
    if (node.next) node.next.prev = newNode;else this.tail = newNode;
    node.next = newNode;
    this.length += 1;
};
 
DLL.prototype.insertBefore = function (node, newNode) {
    newNode.prev = node.prev;
    newNode.next = node;
    if (node.prev) node.prev.next = newNode;else this.head = newNode;
    node.prev = newNode;
    this.length += 1;
};
 
DLL.prototype.unshift = function (node) {
    if (this.head) this.insertBefore(this.head, node);else setInitial(this, node);
};
 
DLL.prototype.push = function (node) {
    if (this.tail) this.insertAfter(this.tail, node);else setInitial(this, node);
};
 
DLL.prototype.shift = function () {
    return this.head && this.removeLink(this.head);
};
 
DLL.prototype.pop = function () {
    return this.tail && this.removeLink(this.tail);
};
 
function queue(worker, concurrency, payload) {
    if (concurrency == null) {
        concurrency = 1;
    } else if (concurrency === 0) {
        throw new Error('Concurrency must not be zero');
    }
 
    var _worker = wrapAsync$1(worker);
    var numRunning = 0;
    var workersList = [];
 
    function _insert(data, insertAtFront, callback) {
        if (callback != null && typeof callback !== 'function') {
            throw new Error('task callback must be a function');
        }
        q.started = true;
        if (!isArray(data)) {
            data = [data];
        }
        if (data.length === 0 && q.idle()) {
            // call drain immediately if there are no tasks
            return setImmediate$1(function () {
                q.drain();
            });
        }
 
        for (var i = 0, l = data.length; i < l; i++) {
            var item = {
                data: data[i],
                callback: callback || noop
            };
 
            if (insertAtFront) {
                q._tasks.unshift(item);
            } else {
                q._tasks.push(item);
            }
        }
        setImmediate$1(q.process);
    }
 
    function _next(tasks) {
        return rest(function (args) {
            numRunning -= 1;
 
            for (var i = 0, l = tasks.length; i < l; i++) {
                var task = tasks[i];
                var index = baseIndexOf(workersList, task, 0);
                if (index >= 0) {
                    workersList.splice(index);
                }
 
                task.callback.apply(task, args);
 
                if (args[0] != null) {
                    q.error(args[0], task.data);
                }
            }
 
            if (numRunning <= q.concurrency - q.buffer) {
                q.unsaturated();
            }
 
            if (q.idle()) {
                q.drain();
            }
            q.process();
        });
    }
 
    var isProcessing = false;
    var q = {
        _tasks: new DLL(),
        concurrency: concurrency,
        payload: payload,
        saturated: noop,
        unsaturated: noop,
        buffer: concurrency / 4,
        empty: noop,
        drain: noop,
        error: noop,
        started: false,
        paused: false,
        push: function (data, callback) {
            _insert(data, false, callback);
        },
        kill: function () {
            q.drain = noop;
            q._tasks.empty();
        },
        unshift: function (data, callback) {
            _insert(data, true, callback);
        },
        process: function () {
            // Avoid trying to start too many processing operations. This can occur
            // when callbacks resolve synchronously (#1267).
            if (isProcessing) {
                return;
            }
            isProcessing = true;
            while (!q.paused && numRunning < q.concurrency && q._tasks.length) {
                var tasks = [],
                    data = [];
                var l = q._tasks.length;
                if (q.payload) l = Math.min(l, q.payload);
                for (var i = 0; i < l; i++) {
                    var node = q._tasks.shift();
                    tasks.push(node);
                    data.push(node.data);
                }
 
                if (q._tasks.length === 0) {
                    q.empty();
                }
                numRunning += 1;
                workersList.push(tasks[0]);
 
                if (numRunning === q.concurrency) {
                    q.saturated();
                }
 
                var cb = onlyOnce(_next(tasks));
                _worker(data, cb);
            }
            isProcessing = false;
        },
        length: function () {
            return q._tasks.length;
        },
        running: function () {
            return numRunning;
        },
        workersList: function () {
            return workersList;
        },
        idle: function () {
            return q._tasks.length + numRunning === 0;
        },
        pause: function () {
            q.paused = true;
        },
        resume: function () {
            if (q.paused === false) {
                return;
            }
            q.paused = false;
            setImmediate$1(q.process);
        }
    };
    return q;
}
 
/**
 * A cargo of tasks for the worker function to complete. Cargo inherits all of
 * the same methods and event callbacks as [`queue`]{@link module:ControlFlow.queue}.
 * @typedef {Object} CargoObject
 * @memberOf module:ControlFlow
 * @property {Function} length - A function returning the number of items
 * waiting to be processed. Invoke like `cargo.length()`.
 * @property {number} payload - An `integer` for determining how many tasks
 * should be process per round. This property can be changed after a `cargo` is
 * created to alter the payload on-the-fly.
 * @property {Function} push - Adds `task` to the `queue`. The callback is
 * called once the `worker` has finished processing the task. Instead of a
 * single task, an array of `tasks` can be submitted. The respective callback is
 * used for every task in the list. Invoke like `cargo.push(task, [callback])`.
 * @property {Function} saturated - A callback that is called when the
 * `queue.length()` hits the concurrency and further tasks will be queued.
 * @property {Function} empty - A callback that is called when the last item
 * from the `queue` is given to a `worker`.
 * @property {Function} drain - A callback that is called when the last item
 * from the `queue` has returned from the `worker`.
 * @property {Function} idle - a function returning false if there are items
 * waiting or being processed, or true if not. Invoke like `cargo.idle()`.
 * @property {Function} pause - a function that pauses the processing of tasks
 * until `resume()` is called. Invoke like `cargo.pause()`.
 * @property {Function} resume - a function that resumes the processing of
 * queued tasks when the queue is paused. Invoke like `cargo.resume()`.
 * @property {Function} kill - a function that removes the `drain` callback and
 * empties remaining tasks from the queue forcing it to go idle. Invoke like `cargo.kill()`.
 */
 
/**
 * Creates a `cargo` object with the specified payload. Tasks added to the
 * cargo will be processed altogether (up to the `payload` limit). If the
 * `worker` is in progress, the task is queued until it becomes available. Once
 * the `worker` has completed some tasks, each callback of those tasks is
 * called. Check out [these](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) [animations](https://camo.githubusercontent.com/f4810e00e1c5f5f8addbe3e9f49064fd5d102699/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130312f38346339323036362d356632392d313165322d383134662d3964336430323431336266642e676966)
 * for how `cargo` and `queue` work.
 *
 * While [`queue`]{@link module:ControlFlow.queue} passes only one task to one of a group of workers
 * at a time, cargo passes an array of tasks to a single worker, repeating
 * when the worker is finished.
 *
 * @name cargo
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.queue]{@link module:ControlFlow.queue}
 * @category Control Flow
 * @param {AsyncFunction} worker - An asynchronous function for processing an array
 * of queued tasks. Invoked with `(tasks, callback)`.
 * @param {number} [payload=Infinity] - An optional `integer` for determining
 * how many tasks should be processed per round; if omitted, the default is
 * unlimited.
 * @returns {module:ControlFlow.CargoObject} A cargo object to manage the tasks. Callbacks can
 * attached as certain properties to listen for specific events during the
 * lifecycle of the cargo and inner queue.
 * @example
 *
 * // create a cargo object with payload 2
 * var cargo = async.cargo(function(tasks, callback) {
 *     for (var i=0; i<tasks.length; i++) {
 *         console.log('hello ' + tasks[i].name);
 *     }
 *     callback();
 * }, 2);
 *
 * // add some items
 * cargo.push({name: 'foo'}, function(err) {
 *     console.log('finished processing foo');
 * });
 * cargo.push({name: 'bar'}, function(err) {
 *     console.log('finished processing bar');
 * });
 * cargo.push({name: 'baz'}, function(err) {
 *     console.log('finished processing baz');
 * });
 */
function cargo(worker, payload) {
  return queue(worker, 1, payload);
}
 
/**
 * The same as [`eachOf`]{@link module:Collections.eachOf} but runs only a single async operation at a time.
 *
 * @name eachOfSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.eachOf]{@link module:Collections.eachOf}
 * @alias forEachOfSeries
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * Invoked with (item, key, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. Invoked with (err).
 */
var eachOfSeries = doLimit(eachOfLimit, 1);
 
/**
 * Reduces `coll` into a single value using an async `iteratee` to return each
 * successive step. `memo` is the initial state of the reduction. This function
 * only operates in series.
 *
 * For performance reasons, it may make sense to split a call to this function
 * into a parallel map, and then use the normal `Array.prototype.reduce` on the
 * results. This function is for situations where each step in the reduction
 * needs to be async; if you can get the data before reducing it, then it's
 * probably a good idea to do so.
 *
 * @name reduce
 * @static
 * @memberOf module:Collections
 * @method
 * @alias inject
 * @alias foldl
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {*} memo - The initial state of the reduction.
 * @param {AsyncFunction} iteratee - A function applied to each item in the
 * array to produce the next step in the reduction.
 * The `iteratee` should complete with the next state of the reduction.
 * If the iteratee complete with an error, the reduction is stopped and the
 * main `callback` is immediately called with the error.
 * Invoked with (memo, item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Result is the reduced value. Invoked with
 * (err, result).
 * @example
 *
 * async.reduce([1,2,3], 0, function(memo, item, callback) {
 *     // pointless async:
 *     process.nextTick(function() {
 *         callback(null, memo + item)
 *     });
 * }, function(err, result) {
 *     // result is now equal to the last value of memo, which is 6
 * });
 */
function reduce(coll, memo, iteratee, callback) {
    callback = once(callback || noop);
    var _iteratee = wrapAsync$1(iteratee);
    eachOfSeries(coll, function (x, i, callback) {
        _iteratee(memo, x, function (err, v) {
            memo = v;
            callback(err);
        });
    }, function (err) {
        callback(err, memo);
    });
}
 
/**
 * Version of the compose function that is more natural to read. Each function
 * consumes the return value of the previous function. It is the equivalent of
 * [compose]{@link module:ControlFlow.compose} with the arguments reversed.
 *
 * Each function is executed with the `this` binding of the composed function.
 *
 * @name seq
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.compose]{@link module:ControlFlow.compose}
 * @category Control Flow
 * @param {...AsyncFunction} functions - the asynchronous functions to compose
 * @returns {Function} a function that composes the `functions` in order
 * @example
 *
 * // Requires lodash (or underscore), express3 and dresende's orm2.
 * // Part of an app, that fetches cats of the logged user.
 * // This example uses `seq` function to avoid overnesting and error
 * // handling clutter.
 * app.get('/cats', function(request, response) {
 *     var User = request.models.User;
 *     async.seq(
 *         _.bind(User.get, User),  // 'User.get' has signature (id, callback(err, data))
 *         function(user, fn) {
 *             user.getCats(fn);      // 'getCats' has signature (callback(err, data))
 *         }
 *     )(req.session.user_id, function (err, cats) {
 *         if (err) {
 *             console.error(err);
 *             response.json({ status: 'error', message: err.message });
 *         } else {
 *             response.json({ status: 'ok', message: 'Cats found', data: cats });
 *         }
 *     });
 * });
 */
var seq$1 = rest(function seq(functions) {
    var _functions = arrayMap(functions, wrapAsync$1);
    return rest(function (args) {
        var that = this;
 
        var cb = args[args.length - 1];
        if (typeof cb == 'function') {
            args.pop();
        } else {
            cb = noop;
        }
 
        reduce(_functions, args, function (newargs, fn, cb) {
            fn.apply(that, newargs.concat(rest(function (err, nextargs) {
                cb(err, nextargs);
            })));
        }, function (err, results) {
            cb.apply(that, [err].concat(results));
        });
    });
});
 
/**
 * Creates a function which is a composition of the passed asynchronous
 * functions. Each function consumes the return value of the function that
 * follows. Composing functions `f()`, `g()`, and `h()` would produce the result
 * of `f(g(h()))`, only this version uses callbacks to obtain the return values.
 *
 * Each function is executed with the `this` binding of the composed function.
 *
 * @name compose
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {...AsyncFunction} functions - the asynchronous functions to compose
 * @returns {Function} an asynchronous function that is the composed
 * asynchronous `functions`
 * @example
 *
 * function add1(n, callback) {
 *     setTimeout(function () {
 *         callback(null, n + 1);
 *     }, 10);
 * }
 *
 * function mul3(n, callback) {
 *     setTimeout(function () {
 *         callback(null, n * 3);
 *     }, 10);
 * }
 *
 * var add1mul3 = async.compose(mul3, add1);
 * add1mul3(4, function (err, result) {
 *     // result now equals 15
 * });
 */
var compose = rest(function (args) {
  return seq$1.apply(null, args.reverse());
});
 
function concat$1(eachfn, arr, fn, callback) {
    var result = [];
    eachfn(arr, function (x, index, cb) {
        fn(x, function (err, y) {
            result = result.concat(y || []);
            cb(err);
        });
    }, function (err) {
        callback(err, result);
    });
}
 
/**
 * Applies `iteratee` to each item in `coll`, concatenating the results. Returns
 * the concatenated list. The `iteratee`s are called in parallel, and the
 * results are concatenated as they return. There is no guarantee that the
 * results array will be returned in the original order of `coll` passed to the
 * `iteratee` function.
 *
 * @name concat
 * @static
 * @memberOf module:Collections
 * @method
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - A function to apply to each item in `coll`,
 * which should use an array as its result. Invoked with (item, callback).
 * @param {Function} [callback(err)] - A callback which is called after all the
 * `iteratee` functions have finished, or an error occurs. Results is an array
 * containing the concatenated results of the `iteratee` function. Invoked with
 * (err, results).
 * @example
 *
 * async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files) {
 *     // files is now a list of filenames that exist in the 3 directories
 * });
 */
var concat = doParallel(concat$1);
 
function doSeries(fn) {
    return function (obj, iteratee, callback) {
        return fn(eachOfSeries, obj, wrapAsync$1(iteratee), callback);
    };
}
 
/**
 * The same as [`concat`]{@link module:Collections.concat} but runs only a single async operation at a time.
 *
 * @name concatSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.concat]{@link module:Collections.concat}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - A function to apply to each item in `coll`.
 * The iteratee should complete with an array an array of results.
 * Invoked with (item, callback).
 * @param {Function} [callback(err)] - A callback which is called after all the
 * `iteratee` functions have finished, or an error occurs. Results is an array
 * containing the concatenated results of the `iteratee` function. Invoked with
 * (err, results).
 */
var concatSeries = doSeries(concat$1);
 
/**
 * Returns a function that when called, calls-back with the values provided.
 * Useful as the first function in a [`waterfall`]{@link module:ControlFlow.waterfall}, or for plugging values in to
 * [`auto`]{@link module:ControlFlow.auto}.
 *
 * @name constant
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {...*} arguments... - Any number of arguments to automatically invoke
 * callback with.
 * @returns {AsyncFunction} Returns a function that when invoked, automatically
 * invokes the callback with the previous given arguments.
 * @example
 *
 * async.waterfall([
 *     async.constant(42),
 *     function (value, next) {
 *         // value === 42
 *     },
 *     //...
 * ], callback);
 *
 * async.waterfall([
 *     async.constant(filename, "utf8"),
 *     fs.readFile,
 *     function (fileData, next) {
 *         //...
 *     }
 *     //...
 * ], callback);
 *
 * async.auto({
 *     hostname: async.constant("https://server.net/"),
 *     port: findFreePort,
 *     launchServer: ["hostname", "port", function (options, cb) {
 *         startServer(options, cb);
 *     }],
 *     //...
 * }, callback);
 */
var constant = rest(function (values) {
    var args = [null].concat(values);
    return initialParams(function (ignoredArgs, callback) {
        return callback.apply(this, args);
    });
});
 
function _createTester(check, getResult) {
    return function (eachfn, arr, iteratee, cb) {
        cb = cb || noop;
        var testPassed = false;
        var testResult;
        eachfn(arr, function (value, _, callback) {
            iteratee(value, function (err, result) {
                if (err) {
                    callback(err);
                } else if (check(result) && !testResult) {
                    testPassed = true;
                    testResult = getResult(true, value);
                    callback(null, breakLoop);
                } else {
                    callback();
                }
            });
        }, function (err) {
            if (err) {
                cb(err);
            } else {
                cb(null, testPassed ? testResult : getResult(false));
            }
        });
    };
}
 
function _findGetResult(v, x) {
    return x;
}
 
/**
 * Returns the first value in `coll` that passes an async truth test. The
 * `iteratee` is applied in parallel, meaning the first iteratee to return
 * `true` will fire the detect `callback` with that result. That means the
 * result might not be the first item in the original `coll` (in terms of order)
 * that passes the test.
 
 * If order within the original `coll` is important, then look at
 * [`detectSeries`]{@link module:Collections.detectSeries}.
 *
 * @name detect
 * @static
 * @memberOf module:Collections
 * @method
 * @alias find
 * @category Collections
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - A truth test to apply to each item in `coll`.
 * The iteratee must complete with a boolean value as its result.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called as soon as any
 * iteratee returns `true`, or after all the `iteratee` functions have finished.
 * Result will be the first item in the array that passes the truth test
 * (iteratee) or the value `undefined` if none passed. Invoked with
 * (err, result).
 * @example
 *
 * async.detect(['file1','file2','file3'], function(filePath, callback) {
 *     fs.access(filePath, function(err) {
 *         callback(null, !err)
 *     });
 * }, function(err, result) {
 *     // result now equals the first file in the list that exists
 * });
 */
var detect = doParallel(_createTester(identity, _findGetResult));
 
/**
 * The same as [`detect`]{@link module:Collections.detect} but runs a maximum of `limit` async operations at a
 * time.
 *
 * @name detectLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.detect]{@link module:Collections.detect}
 * @alias findLimit
 * @category Collections
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - A truth test to apply to each item in `coll`.
 * The iteratee must complete with a boolean value as its result.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called as soon as any
 * iteratee returns `true`, or after all the `iteratee` functions have finished.
 * Result will be the first item in the array that passes the truth test
 * (iteratee) or the value `undefined` if none passed. Invoked with
 * (err, result).
 */
var detectLimit = doParallelLimit(_createTester(identity, _findGetResult));
 
/**
 * The same as [`detect`]{@link module:Collections.detect} but runs only a single async operation at a time.
 *
 * @name detectSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.detect]{@link module:Collections.detect}
 * @alias findSeries
 * @category Collections
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - A truth test to apply to each item in `coll`.
 * The iteratee must complete with a boolean value as its result.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called as soon as any
 * iteratee returns `true`, or after all the `iteratee` functions have finished.
 * Result will be the first item in the array that passes the truth test
 * (iteratee) or the value `undefined` if none passed. Invoked with
 * (err, result).
 */
var detectSeries = doLimit(detectLimit, 1);
 
function consoleFunc(name) {
    return rest(function (fn, args) {
        wrapAsync$1(fn).apply(null, args.concat(rest(function (err, args) {
            if (typeof console === 'object') {
                if (err) {
                    if (console.error) {
                        console.error(err);
                    }
                } else if (console[name]) {
                    arrayEach(args, function (x) {
                        console[name](x);
                    });
                }
            }
        })));
    });
}
 
/**
 * Logs the result of an [`async` function]{@link AsyncFunction} to the
 * `console` using `console.dir` to display the properties of the resulting object.
 * Only works in Node.js or in browsers that support `console.dir` and
 * `console.error` (such as FF and Chrome).
 * If multiple arguments are returned from the async function,
 * `console.dir` is called on each argument in order.
 *
 * @name dir
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {AsyncFunction} function - The function you want to eventually apply
 * all arguments to.
 * @param {...*} arguments... - Any number of arguments to apply to the function.
 * @example
 *
 * // in a module
 * var hello = function(name, callback) {
 *     setTimeout(function() {
 *         callback(null, {hello: name});
 *     }, 1000);
 * };
 *
 * // in the node repl
 * node> async.dir(hello, 'world');
 * {hello: 'world'}
 */
var dir = consoleFunc('dir');
 
/**
 * The post-check version of [`during`]{@link module:ControlFlow.during}. To reflect the difference in
 * the order of operations, the arguments `test` and `fn` are switched.
 *
 * Also a version of [`doWhilst`]{@link module:ControlFlow.doWhilst} with asynchronous `test` function.
 * @name doDuring
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.during]{@link module:ControlFlow.during}
 * @category Control Flow
 * @param {AsyncFunction} fn - An async function which is called each time
 * `test` passes. Invoked with (callback).
 * @param {AsyncFunction} test - asynchronous truth test to perform before each
 * execution of `fn`. Invoked with (...args, callback), where `...args` are the
 * non-error args from the previous callback of `fn`.
 * @param {Function} [callback] - A callback which is called after the test
 * function has failed and repeated execution of `fn` has stopped. `callback`
 * will be passed an error if one occurred, otherwise `null`.
 */
function doDuring(fn, test, callback) {
    callback = onlyOnce(callback || noop);
    var _fn = wrapAsync$1(fn);
    var _test = wrapAsync$1(test);
 
    var next = rest(function (err, args) {
        if (err) return callback(err);
        args.push(check);
        _test.apply(this, args);
    });
 
    function check(err, truth) {
        if (err) return callback(err);
        if (!truth) return callback(null);
        _fn(next);
    }
 
    check(null, true);
}
 
/**
 * The post-check version of [`whilst`]{@link module:ControlFlow.whilst}. To reflect the difference in
 * the order of operations, the arguments `test` and `iteratee` are switched.
 *
 * `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
 *
 * @name doWhilst
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.whilst]{@link module:ControlFlow.whilst}
 * @category Control Flow
 * @param {AsyncFunction} iteratee - A function which is called each time `test`
 * passes. Invoked with (callback).
 * @param {Function} test - synchronous truth test to perform after each
 * execution of `iteratee`. Invoked with any non-error callback results of
 * `iteratee`.
 * @param {Function} [callback] - A callback which is called after the test
 * function has failed and repeated execution of `iteratee` has stopped.
 * `callback` will be passed an error and any arguments passed to the final
 * `iteratee`'s callback. Invoked with (err, [results]);
 */
function doWhilst(iteratee, test, callback) {
    callback = onlyOnce(callback || noop);
    var _iteratee = wrapAsync$1(iteratee);
    var next = rest(function (err, args) {
        if (err) return callback(err);
        if (test.apply(this, args)) return _iteratee(next);
        callback.apply(null, [null].concat(args));
    });
    _iteratee(next);
}
 
/**
 * Like ['doWhilst']{@link module:ControlFlow.doWhilst}, except the `test` is inverted. Note the
 * argument ordering differs from `until`.
 *
 * @name doUntil
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.doWhilst]{@link module:ControlFlow.doWhilst}
 * @category Control Flow
 * @param {AsyncFunction} iteratee - An async function which is called each time
 * `test` fails. Invoked with (callback).
 * @param {Function} test - synchronous truth test to perform after each
 * execution of `iteratee`. Invoked with any non-error callback results of
 * `iteratee`.
 * @param {Function} [callback] - A callback which is called after the test
 * function has passed and repeated execution of `iteratee` has stopped. `callback`
 * will be passed an error and any arguments passed to the final `iteratee`'s
 * callback. Invoked with (err, [results]);
 */
function doUntil(iteratee, test, callback) {
    doWhilst(iteratee, function () {
        return !test.apply(this, arguments);
    }, callback);
}
 
/**
 * Like [`whilst`]{@link module:ControlFlow.whilst}, except the `test` is an asynchronous function that
 * is passed a callback in the form of `function (err, truth)`. If error is
 * passed to `test` or `fn`, the main callback is immediately called with the
 * value of the error.
 *
 * @name during
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.whilst]{@link module:ControlFlow.whilst}
 * @category Control Flow
 * @param {AsyncFunction} test - asynchronous truth test to perform before each
 * execution of `fn`. Invoked with (callback).
 * @param {AsyncFunction} fn - An async function which is called each time
 * `test` passes. Invoked with (callback).
 * @param {Function} [callback] - A callback which is called after the test
 * function has failed and repeated execution of `fn` has stopped. `callback`
 * will be passed an error, if one occurred, otherwise `null`.
 * @example
 *
 * var count = 0;
 *
 * async.during(
 *     function (callback) {
 *         return callback(null, count < 5);
 *     },
 *     function (callback) {
 *         count++;
 *         setTimeout(callback, 1000);
 *     },
 *     function (err) {
 *         // 5 seconds have passed
 *     }
 * );
 */
function during(test, fn, callback) {
    callback = onlyOnce(callback || noop);
    var _fn = wrapAsync$1(fn);
    var _test = wrapAsync$1(test);
 
    function next(err) {
        if (err) return callback(err);
        _test(check);
    }
 
    function check(err, truth) {
        if (err) return callback(err);
        if (!truth) return callback(null);
        _fn(next);
    }
 
    _test(check);
}
 
function _withoutIndex(iteratee) {
    return function (value, index, callback) {
        return iteratee(value, callback);
    };
}
 
/**
 * Applies the function `iteratee` to each item in `coll`, in parallel.
 * The `iteratee` is called with an item from the list, and a callback for when
 * it has finished. If the `iteratee` passes an error to its `callback`, the
 * main `callback` (for the `each` function) is immediately called with the
 * error.
 *
 * Note, that since this function applies `iteratee` to each item in parallel,
 * there is no guarantee that the iteratee functions will complete in order.
 *
 * @name each
 * @static
 * @memberOf module:Collections
 * @method
 * @alias forEach
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async function to apply to
 * each item in `coll`. Invoked with (item, callback).
 * The array index is not passed to the iteratee.
 * If you need the index, use `eachOf`.
 * @param {Function} [callback] - A callback which is called when all
 * `iteratee` functions have finished, or an error occurs. Invoked with (err).
 * @example
 *
 * // assuming openFiles is an array of file names and saveFile is a function
 * // to save the modified contents of that file:
 *
 * async.each(openFiles, saveFile, function(err){
 *   // if any of the saves produced an error, err would equal that error
 * });
 *
 * // assuming openFiles is an array of file names
 * async.each(openFiles, function(file, callback) {
 *
 *     // Perform operation on file here.
 *     console.log('Processing file ' + file);
 *
 *     if( file.length > 32 ) {
 *       console.log('This file name is too long');
 *       callback('File name too long');
 *     } else {
 *       // Do work to process file here
 *       console.log('File processed');
 *       callback();
 *     }
 * }, function(err) {
 *     // if any of the file processing produced an error, err would equal that error
 *     if( err ) {
 *       // One of the iterations produced an error.
 *       // All processing will now stop.
 *       console.log('A file failed to process');
 *     } else {
 *       console.log('All files have been processed successfully');
 *     }
 * });
 */
function eachLimit(coll, iteratee, callback) {
  eachOf(coll, _withoutIndex(wrapAsync$1(iteratee)), callback);
}
 
/**
 * The same as [`each`]{@link module:Collections.each} but runs a maximum of `limit` async operations at a time.
 *
 * @name eachLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.each]{@link module:Collections.each}
 * @alias forEachLimit
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The array index is not passed to the iteratee.
 * If you need the index, use `eachOfLimit`.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called when all
 * `iteratee` functions have finished, or an error occurs. Invoked with (err).
 */
function eachLimit$1(coll, limit, iteratee, callback) {
  _eachOfLimit(limit)(coll, _withoutIndex(wrapAsync$1(iteratee)), callback);
}
 
/**
 * The same as [`each`]{@link module:Collections.each} but runs only a single async operation at a time.
 *
 * @name eachSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.each]{@link module:Collections.each}
 * @alias forEachSeries
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async function to apply to each
 * item in `coll`.
 * The array index is not passed to the iteratee.
 * If you need the index, use `eachOfSeries`.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called when all
 * `iteratee` functions have finished, or an error occurs. Invoked with (err).
 */
var eachSeries = doLimit(eachLimit$1, 1);
 
/**
 * Wrap an async function and ensure it calls its callback on a later tick of
 * the event loop.  If the function already calls its callback on a next tick,
 * no extra deferral is added. This is useful for preventing stack overflows
 * (`RangeError: Maximum call stack size exceeded`) and generally keeping
 * [Zalgo](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony)
 * contained. ES2017 `async` functions are returned as-is -- they are immune
 * to Zalgo's corrupting influences, as they always resolve on a later tick.
 *
 * @name ensureAsync
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {AsyncFunction} fn - an async function, one that expects a node-style
 * callback as its last argument.
 * @returns {AsyncFunction} Returns a wrapped function with the exact same call
 * signature as the function passed in.
 * @example
 *
 * function sometimesAsync(arg, callback) {
 *     if (cache[arg]) {
 *         return callback(null, cache[arg]); // this would be synchronous!!
 *     } else {
 *         doSomeIO(arg, callback); // this IO would be asynchronous
 *     }
 * }
 *
 * // this has a risk of stack overflows if many results are cached in a row
 * async.mapSeries(args, sometimesAsync, done);
 *
 * // this will defer sometimesAsync's callback if necessary,
 * // preventing stack overflows
 * async.mapSeries(args, async.ensureAsync(sometimesAsync), done);
 */
function ensureAsync(fn) {
    if (isAsync(fn)) return fn;
    return initialParams(function (args, callback) {
        var sync = true;
        args.push(function () {
            var innerArgs = arguments;
            if (sync) {
                setImmediate$1(function () {
                    callback.apply(null, innerArgs);
                });
            } else {
                callback.apply(null, innerArgs);
            }
        });
        fn.apply(this, args);
        sync = false;
    });
}
 
function notId(v) {
    return !v;
}
 
/**
 * Returns `true` if every element in `coll` satisfies an async test. If any
 * iteratee call returns `false`, the main `callback` is immediately called.
 *
 * @name every
 * @static
 * @memberOf module:Collections
 * @method
 * @alias all
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async truth test to apply to each item
 * in the collection in parallel.
 * The iteratee must complete with a boolean result value.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Result will be either `true` or `false`
 * depending on the values of the async tests. Invoked with (err, result).
 * @example
 *
 * async.every(['file1','file2','file3'], function(filePath, callback) {
 *     fs.access(filePath, function(err) {
 *         callback(null, !err)
 *     });
 * }, function(err, result) {
 *     // if result is true then every file exists
 * });
 */
var every = doParallel(_createTester(notId, notId));
 
/**
 * The same as [`every`]{@link module:Collections.every} but runs a maximum of `limit` async operations at a time.
 *
 * @name everyLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.every]{@link module:Collections.every}
 * @alias allLimit
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - An async truth test to apply to each item
 * in the collection in parallel.
 * The iteratee must complete with a boolean result value.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Result will be either `true` or `false`
 * depending on the values of the async tests. Invoked with (err, result).
 */
var everyLimit = doParallelLimit(_createTester(notId, notId));
 
/**
 * The same as [`every`]{@link module:Collections.every} but runs only a single async operation at a time.
 *
 * @name everySeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.every]{@link module:Collections.every}
 * @alias allSeries
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async truth test to apply to each item
 * in the collection in series.
 * The iteratee must complete with a boolean result value.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Result will be either `true` or `false`
 * depending on the values of the async tests. Invoked with (err, result).
 */
var everySeries = doLimit(everyLimit, 1);
 
/**
 * The base implementation of `_.property` without support for deep paths.
 *
 * @private
 * @param {string} key The key of the property to get.
 * @returns {Function} Returns the new accessor function.
 */
function baseProperty(key) {
  return function(object) {
    return object == null ? undefined : object[key];
  };
}
 
function filterArray(eachfn, arr, iteratee, callback) {
    var truthValues = new Array(arr.length);
    eachfn(arr, function (x, index, callback) {
        iteratee(x, function (err, v) {
            truthValues[index] = !!v;
            callback(err);
        });
    }, function (err) {
        if (err) return callback(err);
        var results = [];
        for (var i = 0; i < arr.length; i++) {
            if (truthValues[i]) results.push(arr[i]);
        }
        callback(null, results);
    });
}
 
function filterGeneric(eachfn, coll, iteratee, callback) {
    var results = [];
    eachfn(coll, function (x, index, callback) {
        iteratee(x, function (err, v) {
            if (err) {
                callback(err);
            } else {
                if (v) {
                    results.push({ index: index, value: x });
                }
                callback();
            }
        });
    }, function (err) {
        if (err) {
            callback(err);
        } else {
            callback(null, arrayMap(results.sort(function (a, b) {
                return a.index - b.index;
            }), baseProperty('value')));
        }
    });
}
 
function _filter(eachfn, coll, iteratee, callback) {
    var filter = isArrayLike(coll) ? filterArray : filterGeneric;
    filter(eachfn, coll, wrapAsync$1(iteratee), callback || noop);
}
 
/**
 * Returns a new array of all the values in `coll` which pass an async truth
 * test. This operation is performed in parallel, but the results array will be
 * in the same order as the original.
 *
 * @name filter
 * @static
 * @memberOf module:Collections
 * @method
 * @alias select
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {Function} iteratee - A truth test to apply to each item in `coll`.
 * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
 * with a boolean argument once it has completed. Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Invoked with (err, results).
 * @example
 *
 * async.filter(['file1','file2','file3'], function(filePath, callback) {
 *     fs.access(filePath, function(err) {
 *         callback(null, !err)
 *     });
 * }, function(err, results) {
 *     // results now equals an array of the existing files
 * });
 */
var filter = doParallel(_filter);
 
/**
 * The same as [`filter`]{@link module:Collections.filter} but runs a maximum of `limit` async operations at a
 * time.
 *
 * @name filterLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.filter]{@link module:Collections.filter}
 * @alias selectLimit
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {Function} iteratee - A truth test to apply to each item in `coll`.
 * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
 * with a boolean argument once it has completed. Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Invoked with (err, results).
 */
var filterLimit = doParallelLimit(_filter);
 
/**
 * The same as [`filter`]{@link module:Collections.filter} but runs only a single async operation at a time.
 *
 * @name filterSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.filter]{@link module:Collections.filter}
 * @alias selectSeries
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {Function} iteratee - A truth test to apply to each item in `coll`.
 * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
 * with a boolean argument once it has completed. Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Invoked with (err, results)
 */
var filterSeries = doLimit(filterLimit, 1);
 
/**
 * Calls the asynchronous function `fn` with a callback parameter that allows it
 * to call itself again, in series, indefinitely.
 
 * If an error is passed to the callback then `errback` is called with the
 * error, and execution stops, otherwise it will never be called.
 *
 * @name forever
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {AsyncFunction} fn - an async function to call repeatedly.
 * Invoked with (next).
 * @param {Function} [errback] - when `fn` passes an error to it's callback,
 * this function will be called, and execution stops. Invoked with (err).
 * @example
 *
 * async.forever(
 *     function(next) {
 *         // next is suitable for passing to things that need a callback(err [, whatever]);
 *         // it will result in this function being called again.
 *     },
 *     function(err) {
 *         // if next is called with a value in its first parameter, it will appear
 *         // in here as 'err', and execution will stop.
 *     }
 * );
 */
function forever(fn, errback) {
    var done = onlyOnce(errback || noop);
    var task = wrapAsync$1(ensureAsync(fn));
 
    function next(err) {
        if (err) return done(err);
        task(next);
    }
    next();
}
 
/**
 * The same as [`groupBy`]{@link module:Collections.groupBy} but runs a maximum of `limit` async operations at a time.
 *
 * @name groupByLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.groupBy]{@link module:Collections.groupBy}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The iteratee should complete with a `key` to group the value under.
 * Invoked with (value, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. Result is an `Object` whoses
 * properties are arrays of values which returned the corresponding key.
 */
var groupByLimit = function (coll, limit, iteratee, callback) {
    callback = callback || noop;
    var _iteratee = wrapAsync$1(iteratee);
    mapLimit(coll, limit, function (val, callback) {
        _iteratee(val, function (err, key) {
            if (err) return callback(err);
            return callback(null, { key: key, val: val });
        });
    }, function (err, mapResults) {
        var result = {};
        // from MDN, handle object having an `hasOwnProperty` prop
        var hasOwnProperty = Object.prototype.hasOwnProperty;
 
        for (var i = 0; i < mapResults.length; i++) {
            if (mapResults[i]) {
                var key = mapResults[i].key;
                var val = mapResults[i].val;
 
                if (hasOwnProperty.call(result, key)) {
                    result[key].push(val);
                } else {
                    result[key] = [val];
                }
            }
        }
 
        return callback(err, result);
    });
};
 
/**
 * Returns a new object, where each value corresponds to an array of items, from
 * `coll`, that returned the corresponding key. That is, the keys of the object
 * correspond to the values passed to the `iteratee` callback.
 *
 * Note: Since this function applies the `iteratee` to each item in parallel,
 * there is no guarantee that the `iteratee` functions will complete in order.
 * However, the values for each key in the `result` will be in the same order as
 * the original `coll`. For Objects, the values will roughly be in the order of
 * the original Objects' keys (but this can vary across JavaScript engines).
 *
 * @name groupBy
 * @static
 * @memberOf module:Collections
 * @method
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The iteratee should complete with a `key` to group the value under.
 * Invoked with (value, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. Result is an `Object` whoses
 * properties are arrays of values which returned the corresponding key.
 * @example
 *
 * async.groupBy(['userId1', 'userId2', 'userId3'], function(userId, callback) {
 *     db.findById(userId, function(err, user) {
 *         if (err) return callback(err);
 *         return callback(null, user.age);
 *     });
 * }, function(err, result) {
 *     // result is object containing the userIds grouped by age
 *     // e.g. { 30: ['userId1', 'userId3'], 42: ['userId2']};
 * });
 */
var groupBy = doLimit(groupByLimit, Infinity);
 
/**
 * The same as [`groupBy`]{@link module:Collections.groupBy} but runs only a single async operation at a time.
 *
 * @name groupBySeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.groupBy]{@link module:Collections.groupBy}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The iteratee should complete with a `key` to group the value under.
 * Invoked with (value, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. Result is an `Object` whoses
 * properties are arrays of values which returned the corresponding key.
 */
var groupBySeries = doLimit(groupByLimit, 1);
 
/**
 * Logs the result of an `async` function to the `console`. Only works in
 * Node.js or in browsers that support `console.log` and `console.error` (such
 * as FF and Chrome). If multiple arguments are returned from the async
 * function, `console.log` is called on each argument in order.
 *
 * @name log
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {AsyncFunction} function - The function you want to eventually apply
 * all arguments to.
 * @param {...*} arguments... - Any number of arguments to apply to the function.
 * @example
 *
 * // in a module
 * var hello = function(name, callback) {
 *     setTimeout(function() {
 *         callback(null, 'hello ' + name);
 *     }, 1000);
 * };
 *
 * // in the node repl
 * node> async.log(hello, 'world');
 * 'hello world'
 */
var log = consoleFunc('log');
 
/**
 * The same as [`mapValues`]{@link module:Collections.mapValues} but runs a maximum of `limit` async operations at a
 * time.
 *
 * @name mapValuesLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.mapValues]{@link module:Collections.mapValues}
 * @category Collection
 * @param {Object} obj - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - A function to apply to each value and key
 * in `coll`.
 * The iteratee should complete with the transformed value as its result.
 * Invoked with (value, key, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. `result` is a new object consisting
 * of each key from `obj`, with each transformed value on the right-hand side.
 * Invoked with (err, result).
 */
function mapValuesLimit(obj, limit, iteratee, callback) {
    callback = once(callback || noop);
    var newObj = {};
    var _iteratee = wrapAsync$1(iteratee);
    eachOfLimit(obj, limit, function (val, key, next) {
        _iteratee(val, key, function (err, result) {
            if (err) return next(err);
            newObj[key] = result;
            next();
        });
    }, function (err) {
        callback(err, newObj);
    });
}
 
/**
 * A relative of [`map`]{@link module:Collections.map}, designed for use with objects.
 *
 * Produces a new Object by mapping each value of `obj` through the `iteratee`
 * function. The `iteratee` is called each `value` and `key` from `obj` and a
 * callback for when it has finished processing. Each of these callbacks takes
 * two arguments: an `error`, and the transformed item from `obj`. If `iteratee`
 * passes an error to its callback, the main `callback` (for the `mapValues`
 * function) is immediately called with the error.
 *
 * Note, the order of the keys in the result is not guaranteed.  The keys will
 * be roughly in the order they complete, (but this is very engine-specific)
 *
 * @name mapValues
 * @static
 * @memberOf module:Collections
 * @method
 * @category Collection
 * @param {Object} obj - A collection to iterate over.
 * @param {AsyncFunction} iteratee - A function to apply to each value and key
 * in `coll`.
 * The iteratee should complete with the transformed value as its result.
 * Invoked with (value, key, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. `result` is a new object consisting
 * of each key from `obj`, with each transformed value on the right-hand side.
 * Invoked with (err, result).
 * @example
 *
 * async.mapValues({
 *     f1: 'file1',
 *     f2: 'file2',
 *     f3: 'file3'
 * }, function (file, key, callback) {
 *   fs.stat(file, callback);
 * }, function(err, result) {
 *     // result is now a map of stats for each file, e.g.
 *     // {
 *     //     f1: [stats for file1],
 *     //     f2: [stats for file2],
 *     //     f3: [stats for file3]
 *     // }
 * });
 */
 
var mapValues = doLimit(mapValuesLimit, Infinity);
 
/**
 * The same as [`mapValues`]{@link module:Collections.mapValues} but runs only a single async operation at a time.
 *
 * @name mapValuesSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.mapValues]{@link module:Collections.mapValues}
 * @category Collection
 * @param {Object} obj - A collection to iterate over.
 * @param {AsyncFunction} iteratee - A function to apply to each value and key
 * in `coll`.
 * The iteratee should complete with the transformed value as its result.
 * Invoked with (value, key, callback).
 * @param {Function} [callback] - A callback which is called when all `iteratee`
 * functions have finished, or an error occurs. `result` is a new object consisting
 * of each key from `obj`, with each transformed value on the right-hand side.
 * Invoked with (err, result).
 */
var mapValuesSeries = doLimit(mapValuesLimit, 1);
 
function has(obj, key) {
    return key in obj;
}
 
/**
 * Caches the results of an async function. When creating a hash to store
 * function results against, the callback is omitted from the hash and an
 * optional hash function can be used.
 *
 * If no hash function is specified, the first argument is used as a hash key,
 * which may work reasonably if it is a string or a data type that converts to a
 * distinct string. Note that objects and arrays will not behave reasonably.
 * Neither will cases where the other arguments are significant. In such cases,
 * specify your own hash function.
 *
 * The cache of results is exposed as the `memo` property of the function
 * returned by `memoize`.
 *
 * @name memoize
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {AsyncFunction} fn - The async function to proxy and cache results from.
 * @param {Function} hasher - An optional function for generating a custom hash
 * for storing results. It has all the arguments applied to it apart from the
 * callback, and must be synchronous.
 * @returns {AsyncFunction} a memoized version of `fn`
 * @example
 *
 * var slow_fn = function(name, callback) {
 *     // do something
 *     callback(null, result);
 * };
 * var fn = async.memoize(slow_fn);
 *
 * // fn can now be used as if it were slow_fn
 * fn('some name', function() {
 *     // callback
 * });
 */
function memoize(fn, hasher) {
    var memo = Object.create(null);
    var queues = Object.create(null);
    hasher = hasher || identity;
    var _fn = wrapAsync$1(fn);
    var memoized = initialParams(function memoized(args, callback) {
        var key = hasher.apply(null, args);
        if (has(memo, key)) {
            setImmediate$1(function () {
                callback.apply(null, memo[key]);
            });
        } else if (has(queues, key)) {
            queues[key].push(callback);
        } else {
            queues[key] = [callback];
            _fn.apply(null, args.concat(rest(function (args) {
                memo[key] = args;
                var q = queues[key];
                delete queues[key];
                for (var i = 0, l = q.length; i < l; i++) {
                    q[i].apply(null, args);
                }
            })));
        }
    });
    memoized.memo = memo;
    memoized.unmemoized = fn;
    return memoized;
}
 
/**
 * Calls `callback` on a later loop around the event loop. In Node.js this just
 * calls `setImmediate`.  In the browser it will use `setImmediate` if
 * available, otherwise `setTimeout(callback, 0)`, which means other higher
 * priority events may precede the execution of `callback`.
 *
 * This is used internally for browser-compatibility purposes.
 *
 * @name nextTick
 * @static
 * @memberOf module:Utils
 * @method
 * @alias setImmediate
 * @category Util
 * @param {Function} callback - The function to call on a later loop around
 * the event loop. Invoked with (args...).
 * @param {...*} args... - any number of additional arguments to pass to the
 * callback on the next tick.
 * @example
 *
 * var call_order = [];
 * async.nextTick(function() {
 *     call_order.push('two');
 *     // call_order now equals ['one','two']
 * });
 * call_order.push('one');
 *
 * async.setImmediate(function (a, b, c) {
 *     // a, b, and c equal 1, 2, and 3
 * }, 1, 2, 3);
 */
var _defer$1;
 
Eif (hasNextTick) {
    _defer$1 = process.nextTick;
} else if (hasSetImmediate) {
    _defer$1 = setImmediate;
} else {
    _defer$1 = fallback;
}
 
var nextTick = wrap(_defer$1);
 
function _parallel(eachfn, tasks, callback) {
    callback = callback || noop;
    var results = isArrayLike(tasks) ? [] : {};
 
    eachfn(tasks, function (task, key, callback) {
        wrapAsync$1(task)(rest(function (err, args) {
            if (args.length <= 1) {
                args = args[0];
            }
            results[key] = args;
            callback(err);
        }));
    }, function (err) {
        callback(err, results);
    });
}
 
/**
 * Run the `tasks` collection of functions in parallel, without waiting until
 * the previous function has completed. If any of the functions pass an error to
 * its callback, the main `callback` is immediately called with the value of the
 * error. Once the `tasks` have completed, the results are passed to the final
 * `callback` as an array.
 *
 * **Note:** `parallel` is about kicking-off I/O tasks in parallel, not about
 * parallel execution of code.  If your tasks do not use any timers or perform
 * any I/O, they will actually be executed in series.  Any synchronous setup
 * sections for each task will happen one after the other.  JavaScript remains
 * single-threaded.
 *
 * **Hint:** Use [`reflect`]{@link module:Utils.reflect} to continue the
 * execution of other tasks when a task fails.
 *
 * It is also possible to use an object instead of an array. Each property will
 * be run as a function and the results will be passed to the final `callback`
 * as an object instead of an array. This can be a more readable way of handling
 * results from {@link async.parallel}.
 *
 * @name parallel
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {Array|Iterable|Object} tasks - A collection of
 * [async functions]{@link AsyncFunction} to run.
 * Each async function can complete with any number of optional `result` values.
 * @param {Function} [callback] - An optional callback to run once all the
 * functions have completed successfully. This function gets a results array
 * (or object) containing all the result arguments passed to the task callbacks.
 * Invoked with (err, results).
 *
 * @example
 * async.parallel([
 *     function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'one');
 *         }, 200);
 *     },
 *     function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'two');
 *         }, 100);
 *     }
 * ],
 * // optional callback
 * function(err, results) {
 *     // the results array will equal ['one','two'] even though
 *     // the second function had a shorter timeout.
 * });
 *
 * // an example using an object instead of an array
 * async.parallel({
 *     one: function(callback) {
 *         setTimeout(function() {
 *             callback(null, 1);
 *         }, 200);
 *     },
 *     two: function(callback) {
 *         setTimeout(function() {
 *             callback(null, 2);
 *         }, 100);
 *     }
 * }, function(err, results) {
 *     // results is now equals to: {one: 1, two: 2}
 * });
 */
function parallelLimit(tasks, callback) {
  _parallel(eachOf, tasks, callback);
}
 
/**
 * The same as [`parallel`]{@link module:ControlFlow.parallel} but runs a maximum of `limit` async operations at a
 * time.
 *
 * @name parallelLimit
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.parallel]{@link module:ControlFlow.parallel}
 * @category Control Flow
 * @param {Array|Iterable|Object} tasks - A collection of
 * [async functions]{@link AsyncFunction} to run.
 * Each async function can complete with any number of optional `result` values.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {Function} [callback] - An optional callback to run once all the
 * functions have completed successfully. This function gets a results array
 * (or object) containing all the result arguments passed to the task callbacks.
 * Invoked with (err, results).
 */
function parallelLimit$1(tasks, limit, callback) {
  _parallel(_eachOfLimit(limit), tasks, callback);
}
 
/**
 * A queue of tasks for the worker function to complete.
 * @typedef {Object} QueueObject
 * @memberOf module:ControlFlow
 * @property {Function} length - a function returning the number of items
 * waiting to be processed. Invoke with `queue.length()`.
 * @property {boolean} started - a boolean indicating whether or not any
 * items have been pushed and processed by the queue.
 * @property {Function} running - a function returning the number of items
 * currently being processed. Invoke with `queue.running()`.
 * @property {Function} workersList - a function returning the array of items
 * currently being processed. Invoke with `queue.workersList()`.
 * @property {Function} idle - a function returning false if there are items
 * waiting or being processed, or true if not. Invoke with `queue.idle()`.
 * @property {number} concurrency - an integer for determining how many `worker`
 * functions should be run in parallel. This property can be changed after a
 * `queue` is created to alter the concurrency on-the-fly.
 * @property {Function} push - add a new task to the `queue`. Calls `callback`
 * once the `worker` has finished processing the task. Instead of a single task,
 * a `tasks` array can be submitted. The respective callback is used for every
 * task in the list. Invoke with `queue.push(task, [callback])`,
 * @property {Function} unshift - add a new task to the front of the `queue`.
 * Invoke with `queue.unshift(task, [callback])`.
 * @property {Function} saturated - a callback that is called when the number of
 * running workers hits the `concurrency` limit, and further tasks will be
 * queued.
 * @property {Function} unsaturated - a callback that is called when the number
 * of running workers is less than the `concurrency` & `buffer` limits, and
 * further tasks will not be queued.
 * @property {number} buffer - A minimum threshold buffer in order to say that
 * the `queue` is `unsaturated`.
 * @property {Function} empty - a callback that is called when the last item
 * from the `queue` is given to a `worker`.
 * @property {Function} drain - a callback that is called when the last item
 * from the `queue` has returned from the `worker`.
 * @property {Function} error - a callback that is called when a task errors.
 * Has the signature `function(error, task)`.
 * @property {boolean} paused - a boolean for determining whether the queue is
 * in a paused state.
 * @property {Function} pause - a function that pauses the processing of tasks
 * until `resume()` is called. Invoke with `queue.pause()`.
 * @property {Function} resume - a function that resumes the processing of
 * queued tasks when the queue is paused. Invoke with `queue.resume()`.
 * @property {Function} kill - a function that removes the `drain` callback and
 * empties remaining tasks from the queue forcing it to go idle. Invoke with `queue.kill()`.
 */
 
/**
 * Creates a `queue` object with the specified `concurrency`. Tasks added to the
 * `queue` are processed in parallel (up to the `concurrency` limit). If all
 * `worker`s are in progress, the task is queued until one becomes available.
 * Once a `worker` completes a `task`, that `task`'s callback is called.
 *
 * @name queue
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {AsyncFunction} worker - An async function for processing a queued task.
 * If you want to handle errors from an individual task, pass a callback to
 * `q.push()`. Invoked with (task, callback).
 * @param {number} [concurrency=1] - An `integer` for determining how many
 * `worker` functions should be run in parallel.  If omitted, the concurrency
 * defaults to `1`.  If the concurrency is `0`, an error is thrown.
 * @returns {module:ControlFlow.QueueObject} A queue object to manage the tasks. Callbacks can
 * attached as certain properties to listen for specific events during the
 * lifecycle of the queue.
 * @example
 *
 * // create a queue object with concurrency 2
 * var q = async.queue(function(task, callback) {
 *     console.log('hello ' + task.name);
 *     callback();
 * }, 2);
 *
 * // assign a callback
 * q.drain = function() {
 *     console.log('all items have been processed');
 * };
 *
 * // add some items to the queue
 * q.push({name: 'foo'}, function(err) {
 *     console.log('finished processing foo');
 * });
 * q.push({name: 'bar'}, function (err) {
 *     console.log('finished processing bar');
 * });
 *
 * // add some items to the queue (batch-wise)
 * q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
 *     console.log('finished processing item');
 * });
 *
 * // add some items to the front of the queue
 * q.unshift({name: 'bar'}, function (err) {
 *     console.log('finished processing bar');
 * });
 */
var queue$1 = function (worker, concurrency) {
  var _worker = wrapAsync$1(worker);
  return queue(function (items, cb) {
    _worker(items[0], cb);
  }, concurrency, 1);
};
 
/**
 * The same as [async.queue]{@link module:ControlFlow.queue} only tasks are assigned a priority and
 * completed in ascending priority order.
 *
 * @name priorityQueue
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.queue]{@link module:ControlFlow.queue}
 * @category Control Flow
 * @param {AsyncFunction} worker - An async function for processing a queued task.
 * If you want to handle errors from an individual task, pass a callback to
 * `q.push()`.
 * Invoked with (task, callback).
 * @param {number} concurrency - An `integer` for determining how many `worker`
 * functions should be run in parallel.  If omitted, the concurrency defaults to
 * `1`.  If the concurrency is `0`, an error is thrown.
 * @returns {module:ControlFlow.QueueObject} A priorityQueue object to manage the tasks. There are two
 * differences between `queue` and `priorityQueue` objects:
 * * `push(task, priority, [callback])` - `priority` should be a number. If an
 *   array of `tasks` is given, all tasks will be assigned the same priority.
 * * The `unshift` method was removed.
 */
var priorityQueue = function (worker, concurrency) {
    // Start with a normal queue
    var q = queue$1(worker, concurrency);
 
    // Override push to accept second parameter representing priority
    q.push = function (data, priority, callback) {
        if (callback == null) callback = noop;
        if (typeof callback !== 'function') {
            throw new Error('task callback must be a function');
        }
        q.started = true;
        if (!isArray(data)) {
            data = [data];
        }
        if (data.length === 0) {
            // call drain immediately if there are no tasks
            return setImmediate$1(function () {
                q.drain();
            });
        }
 
        priority = priority || 0;
        var nextNode = q._tasks.head;
        while (nextNode && priority >= nextNode.priority) {
            nextNode = nextNode.next;
        }
 
        for (var i = 0, l = data.length; i < l; i++) {
            var item = {
                data: data[i],
                priority: priority,
                callback: callback
            };
 
            if (nextNode) {
                q._tasks.insertBefore(nextNode, item);
            } else {
                q._tasks.push(item);
            }
        }
        setImmediate$1(q.process);
    };
 
    // Remove unshift function
    delete q.unshift;
 
    return q;
};
 
/**
 * Runs the `tasks` array of functions in parallel, without waiting until the
 * previous function has completed. Once any of the `tasks` complete or pass an
 * error to its callback, the main `callback` is immediately called. It's
 * equivalent to `Promise.race()`.
 *
 * @name race
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {Array} tasks - An array containing [async functions]{@link AsyncFunction}
 * to run. Each function can complete with an optional `result` value.
 * @param {Function} callback - A callback to run once any of the functions have
 * completed. This function gets an error or result from the first function that
 * completed. Invoked with (err, result).
 * @returns undefined
 * @example
 *
 * async.race([
 *     function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'one');
 *         }, 200);
 *     },
 *     function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'two');
 *         }, 100);
 *     }
 * ],
 * // main callback
 * function(err, result) {
 *     // the result will be equal to 'two' as it finishes earlier
 * });
 */
function race(tasks, callback) {
    callback = once(callback || noop);
    if (!isArray(tasks)) return callback(new TypeError('First argument to race must be an array of functions'));
    if (!tasks.length) return callback();
    for (var i = 0, l = tasks.length; i < l; i++) {
        wrapAsync$1(tasks[i])(callback);
    }
}
 
var slice = Array.prototype.slice;
 
/**
 * Same as [`reduce`]{@link module:Collections.reduce}, only operates on `array` in reverse order.
 *
 * @name reduceRight
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.reduce]{@link module:Collections.reduce}
 * @alias foldr
 * @category Collection
 * @param {Array} array - A collection to iterate over.
 * @param {*} memo - The initial state of the reduction.
 * @param {AsyncFunction} iteratee - A function applied to each item in the
 * array to produce the next step in the reduction.
 * The `iteratee` should complete with the next state of the reduction.
 * If the iteratee complete with an error, the reduction is stopped and the
 * main `callback` is immediately called with the error.
 * Invoked with (memo, item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Result is the reduced value. Invoked with
 * (err, result).
 */
function reduceRight(array, memo, iteratee, callback) {
  var reversed = slice.call(array).reverse();
  reduce(reversed, memo, iteratee, callback);
}
 
/**
 * Wraps the async function in another function that always completes with a
 * result object, even when it errors.
 *
 * The result object has either the property `error` or `value`.
 *
 * @name reflect
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {AsyncFunction} fn - The async function you want to wrap
 * @returns {Function} - A function that always passes null to it's callback as
 * the error. The second argument to the callback will be an `object` with
 * either an `error` or a `value` property.
 * @example
 *
 * async.parallel([
 *     async.reflect(function(callback) {
 *         // do some stuff ...
 *         callback(null, 'one');
 *     }),
 *     async.reflect(function(callback) {
 *         // do some more stuff but error ...
 *         callback('bad stuff happened');
 *     }),
 *     async.reflect(function(callback) {
 *         // do some more stuff ...
 *         callback(null, 'two');
 *     })
 * ],
 * // optional callback
 * function(err, results) {
 *     // values
 *     // results[0].value = 'one'
 *     // results[1].error = 'bad stuff happened'
 *     // results[2].value = 'two'
 * });
 */
function reflect(fn) {
    var _fn = wrapAsync$1(fn);
    return initialParams(function reflectOn(args, reflectCallback) {
        args.push(rest(function callback(err, cbArgs) {
            if (err) {
                reflectCallback(null, {
                    error: err
                });
            } else {
                var value = null;
                if (cbArgs.length === 1) {
                    value = cbArgs[0];
                } else if (cbArgs.length > 1) {
                    value = cbArgs;
                }
                reflectCallback(null, {
                    value: value
                });
            }
        }));
 
        return _fn.apply(this, args);
    });
}
 
function reject$1(eachfn, arr, iteratee, callback) {
    _filter(eachfn, arr, function (value, cb) {
        iteratee(value, function (err, v) {
            cb(err, !v);
        });
    }, callback);
}
 
/**
 * The opposite of [`filter`]{@link module:Collections.filter}. Removes values that pass an `async` truth test.
 *
 * @name reject
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.filter]{@link module:Collections.filter}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {Function} iteratee - An async truth test to apply to each item in
 * `coll`.
 * The should complete with a boolean value as its `result`.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Invoked with (err, results).
 * @example
 *
 * async.reject(['file1','file2','file3'], function(filePath, callback) {
 *     fs.access(filePath, function(err) {
 *         callback(null, !err)
 *     });
 * }, function(err, results) {
 *     // results now equals an array of missing files
 *     createFiles(results);
 * });
 */
var reject = doParallel(reject$1);
 
/**
 * A helper function that wraps an array or an object of functions with `reflect`.
 *
 * @name reflectAll
 * @static
 * @memberOf module:Utils
 * @method
 * @see [async.reflect]{@link module:Utils.reflect}
 * @category Util
 * @param {Array|Object|Iterable} tasks - The collection of
 * [async functions]{@link AsyncFunction} to wrap in `async.reflect`.
 * @returns {Array} Returns an array of async functions, each wrapped in
 * `async.reflect`
 * @example
 *
 * let tasks = [
 *     function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'one');
 *         }, 200);
 *     },
 *     function(callback) {
 *         // do some more stuff but error ...
 *         callback(new Error('bad stuff happened'));
 *     },
 *     function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'two');
 *         }, 100);
 *     }
 * ];
 *
 * async.parallel(async.reflectAll(tasks),
 * // optional callback
 * function(err, results) {
 *     // values
 *     // results[0].value = 'one'
 *     // results[1].error = Error('bad stuff happened')
 *     // results[2].value = 'two'
 * });
 *
 * // an example using an object instead of an array
 * let tasks = {
 *     one: function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'one');
 *         }, 200);
 *     },
 *     two: function(callback) {
 *         callback('two');
 *     },
 *     three: function(callback) {
 *         setTimeout(function() {
 *             callback(null, 'three');
 *         }, 100);
 *     }
 * };
 *
 * async.parallel(async.reflectAll(tasks),
 * // optional callback
 * function(err, results) {
 *     // values
 *     // results.one.value = 'one'
 *     // results.two.error = 'two'
 *     // results.three.value = 'three'
 * });
 */
function reflectAll(tasks) {
    var results;
    if (isArray(tasks)) {
        results = arrayMap(tasks, reflect);
    } else {
        results = {};
        baseForOwn(tasks, function (task, key) {
            results[key] = reflect.call(this, task);
        });
    }
    return results;
}
 
/**
 * The same as [`reject`]{@link module:Collections.reject} but runs a maximum of `limit` async operations at a
 * time.
 *
 * @name rejectLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.reject]{@link module:Collections.reject}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {Function} iteratee - An async truth test to apply to each item in
 * `coll`.
 * The should complete with a boolean value as its `result`.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Invoked with (err, results).
 */
var rejectLimit = doParallelLimit(reject$1);
 
/**
 * The same as [`reject`]{@link module:Collections.reject} but runs only a single async operation at a time.
 *
 * @name rejectSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.reject]{@link module:Collections.reject}
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {Function} iteratee - An async truth test to apply to each item in
 * `coll`.
 * The should complete with a boolean value as its `result`.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Invoked with (err, results).
 */
var rejectSeries = doLimit(rejectLimit, 1);
 
/**
 * Creates a function that returns `value`.
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Util
 * @param {*} value The value to return from the new function.
 * @returns {Function} Returns the new constant function.
 * @example
 *
 * var objects = _.times(2, _.constant({ 'a': 1 }));
 *
 * console.log(objects);
 * // => [{ 'a': 1 }, { 'a': 1 }]
 *
 * console.log(objects[0] === objects[1]);
 * // => true
 */
function constant$1(value) {
  return function() {
    return value;
  };
}
 
/**
 * Attempts to get a successful response from `task` no more than `times` times
 * before returning an error. If the task is successful, the `callback` will be
 * passed the result of the successful task. If all attempts fail, the callback
 * will be passed the error and result (if any) of the final attempt.
 *
 * @name retry
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @see [async.retryable]{@link module:ControlFlow.retryable}
 * @param {Object|number} [opts = {times: 5, interval: 0}| 5] - Can be either an
 * object with `times` and `interval` or a number.
 * * `times` - The number of attempts to make before giving up.  The default
 *   is `5`.
 * * `interval` - The time to wait between retries, in milliseconds.  The
 *   default is `0`. The interval may also be specified as a function of the
 *   retry count (see example).
 * * `errorFilter` - An optional synchronous function that is invoked on
 *   erroneous result. If it returns `true` the retry attempts will continue;
 *   if the function returns `false` the retry flow is aborted with the current
 *   attempt's error and result being returned to the final callback.
 *   Invoked with (err).
 * * If `opts` is a number, the number specifies the number of times to retry,
 *   with the default interval of `0`.
 * @param {AsyncFunction} task - An async function to retry.
 * Invoked with (callback).
 * @param {Function} [callback] - An optional callback which is called when the
 * task has succeeded, or after the final failed attempt. It receives the `err`
 * and `result` arguments of the last attempt at completing the `task`. Invoked
 * with (err, results).
 *
 * @example
 *
 * // The `retry` function can be used as a stand-alone control flow by passing
 * // a callback, as shown below:
 *
 * // try calling apiMethod 3 times
 * async.retry(3, apiMethod, function(err, result) {
 *     // do something with the result
 * });
 *
 * // try calling apiMethod 3 times, waiting 200 ms between each retry
 * async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
 *     // do something with the result
 * });
 *
 * // try calling apiMethod 10 times with exponential backoff
 * // (i.e. intervals of 100, 200, 400, 800, 1600, ... milliseconds)
 * async.retry({
 *   times: 10,
 *   interval: function(retryCount) {
 *     return 50 * Math.pow(2, retryCount);
 *   }
 * }, apiMethod, function(err, result) {
 *     // do something with the result
 * });
 *
 * // try calling apiMethod the default 5 times no delay between each retry
 * async.retry(apiMethod, function(err, result) {
 *     // do something with the result
 * });
 *
 * // try calling apiMethod only when error condition satisfies, all other
 * // errors will abort the retry control flow and return to final callback
 * async.retry({
 *   errorFilter: function(err) {
 *     return err.message === 'Temporary error'; // only retry on a specific error
 *   }
 * }, apiMethod, function(err, result) {
 *     // do something with the result
 * });
 *
 * // It can also be embedded within other control flow functions to retry
 * // individual methods that are not as reliable, like this:
 * async.auto({
 *     users: api.getUsers.bind(api),
 *     payments: async.retryable(3, api.getPayments.bind(api))
 * }, function(err, results) {
 *     // do something with the results
 * });
 *
 */
function retry(opts, task, callback) {
    var DEFAULT_TIMES = 5;
    var DEFAULT_INTERVAL = 0;
 
    var options = {
        times: DEFAULT_TIMES,
        intervalFunc: constant$1(DEFAULT_INTERVAL)
    };
 
    function parseTimes(acc, t) {
        if (typeof t === 'object') {
            acc.times = +t.times || DEFAULT_TIMES;
 
            acc.intervalFunc = typeof t.interval === 'function' ? t.interval : constant$1(+t.interval || DEFAULT_INTERVAL);
 
            acc.errorFilter = t.errorFilter;
        } else if (typeof t === 'number' || typeof t === 'string') {
            acc.times = +t || DEFAULT_TIMES;
        } else {
            throw new Error("Invalid arguments for async.retry");
        }
    }
 
    if (arguments.length < 3 && typeof opts === 'function') {
        callback = task || noop;
        task = opts;
    } else {
        parseTimes(options, opts);
        callback = callback || noop;
    }
 
    if (typeof task !== 'function') {
        throw new Error("Invalid arguments for async.retry");
    }
 
    var _task = wrapAsync$1(task);
 
    var attempt = 1;
    function retryAttempt() {
        _task(function (err) {
            if (err && attempt++ < options.times && (typeof options.errorFilter != 'function' || options.errorFilter(err))) {
                setTimeout(retryAttempt, options.intervalFunc(attempt));
            } else {
                callback.apply(null, arguments);
            }
        });
    }
 
    retryAttempt();
}
 
/**
 * A close relative of [`retry`]{@link module:ControlFlow.retry}.  This method
 * wraps a task and makes it retryable, rather than immediately calling it
 * with retries.
 *
 * @name retryable
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.retry]{@link module:ControlFlow.retry}
 * @category Control Flow
 * @param {Object|number} [opts = {times: 5, interval: 0}| 5] - optional
 * options, exactly the same as from `retry`
 * @param {AsyncFunction} task - the asynchronous function to wrap.
 * This function will be passed any arguments passed to the returned wrapper.
 * Invoked with (...args, callback).
 * @returns {AsyncFunction} The wrapped function, which when invoked, will
 * retry on an error, based on the parameters specified in `opts`.
 * This function will accept the same parameters as `task`.
 * @example
 *
 * async.auto({
 *     dep1: async.retryable(3, getFromFlakyService),
 *     process: ["dep1", async.retryable(3, function (results, cb) {
 *         maybeProcessData(results.dep1, cb);
 *     })]
 * }, callback);
 */
var retryable = function (opts, task) {
    if (!task) {
        task = opts;
        opts = null;
    }
    var _task = wrapAsync$1(task);
    return initialParams(function (args, callback) {
        function taskFn(cb) {
            _task.apply(null, args.concat(cb));
        }
 
        if (opts) retry(opts, taskFn, callback);else retry(taskFn, callback);
    });
};
 
/**
 * Run the functions in the `tasks` collection in series, each one running once
 * the previous function has completed. If any functions in the series pass an
 * error to its callback, no more functions are run, and `callback` is
 * immediately called with the value of the error. Otherwise, `callback`
 * receives an array of results when `tasks` have completed.
 *
 * It is also possible to use an object instead of an array. Each property will
 * be run as a function, and the results will be passed to the final `callback`
 * as an object instead of an array. This can be a more readable way of handling
 *  results from {@link async.series}.
 *
 * **Note** that while many implementations preserve the order of object
 * properties, the [ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
 * explicitly states that
 *
 * > The mechanics and order of enumerating the properties is not specified.
 *
 * So if you rely on the order in which your series of functions are executed,
 * and want this to work on all platforms, consider using an array.
 *
 * @name series
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {Array|Iterable|Object} tasks - A collection containing
 * [async functions]{@link AsyncFunction} to run in series.
 * Each function can complete with any number of optional `result` values.
 * @param {Function} [callback] - An optional callback to run once all the
 * functions have completed. This function gets a results array (or object)
 * containing all the result arguments passed to the `task` callbacks. Invoked
 * with (err, result).
 * @example
 * async.series([
 *     function(callback) {
 *         // do some stuff ...
 *         callback(null, 'one');
 *     },
 *     function(callback) {
 *         // do some more stuff ...
 *         callback(null, 'two');
 *     }
 * ],
 * // optional callback
 * function(err, results) {
 *     // results is now equal to ['one', 'two']
 * });
 *
 * async.series({
 *     one: function(callback) {
 *         setTimeout(function() {
 *             callback(null, 1);
 *         }, 200);
 *     },
 *     two: function(callback){
 *         setTimeout(function() {
 *             callback(null, 2);
 *         }, 100);
 *     }
 * }, function(err, results) {
 *     // results is now equal to: {one: 1, two: 2}
 * });
 */
function series(tasks, callback) {
  _parallel(eachOfSeries, tasks, callback);
}
 
/**
 * Returns `true` if at least one element in the `coll` satisfies an async test.
 * If any iteratee call returns `true`, the main `callback` is immediately
 * called.
 *
 * @name some
 * @static
 * @memberOf module:Collections
 * @method
 * @alias any
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async truth test to apply to each item
 * in the collections in parallel.
 * The iteratee should complete with a boolean `result` value.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called as soon as any
 * iteratee returns `true`, or after all the iteratee functions have finished.
 * Result will be either `true` or `false` depending on the values of the async
 * tests. Invoked with (err, result).
 * @example
 *
 * async.some(['file1','file2','file3'], function(filePath, callback) {
 *     fs.access(filePath, function(err) {
 *         callback(null, !err)
 *     });
 * }, function(err, result) {
 *     // if result is true then at least one of the files exists
 * });
 */
var some = doParallel(_createTester(Boolean, identity));
 
/**
 * The same as [`some`]{@link module:Collections.some} but runs a maximum of `limit` async operations at a time.
 *
 * @name someLimit
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.some]{@link module:Collections.some}
 * @alias anyLimit
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - An async truth test to apply to each item
 * in the collections in parallel.
 * The iteratee should complete with a boolean `result` value.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called as soon as any
 * iteratee returns `true`, or after all the iteratee functions have finished.
 * Result will be either `true` or `false` depending on the values of the async
 * tests. Invoked with (err, result).
 */
var someLimit = doParallelLimit(_createTester(Boolean, identity));
 
/**
 * The same as [`some`]{@link module:Collections.some} but runs only a single async operation at a time.
 *
 * @name someSeries
 * @static
 * @memberOf module:Collections
 * @method
 * @see [async.some]{@link module:Collections.some}
 * @alias anySeries
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async truth test to apply to each item
 * in the collections in series.
 * The iteratee should complete with a boolean `result` value.
 * Invoked with (item, callback).
 * @param {Function} [callback] - A callback which is called as soon as any
 * iteratee returns `true`, or after all the iteratee functions have finished.
 * Result will be either `true` or `false` depending on the values of the async
 * tests. Invoked with (err, result).
 */
var someSeries = doLimit(someLimit, 1);
 
/**
 * Sorts a list by the results of running each `coll` value through an async
 * `iteratee`.
 *
 * @name sortBy
 * @static
 * @memberOf module:Collections
 * @method
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {AsyncFunction} iteratee - An async function to apply to each item in
 * `coll`.
 * The iteratee should complete with a value to use as the sort criteria as
 * its `result`.
 * Invoked with (item, callback).
 * @param {Function} callback - A callback which is called after all the
 * `iteratee` functions have finished, or an error occurs. Results is the items
 * from the original `coll` sorted by the values returned by the `iteratee`
 * calls. Invoked with (err, results).
 * @example
 *
 * async.sortBy(['file1','file2','file3'], function(file, callback) {
 *     fs.stat(file, function(err, stats) {
 *         callback(err, stats.mtime);
 *     });
 * }, function(err, results) {
 *     // results is now the original array of files sorted by
 *     // modified date
 * });
 *
 * // By modifying the callback parameter the
 * // sorting order can be influenced:
 *
 * // ascending order
 * async.sortBy([1,9,3,5], function(x, callback) {
 *     callback(null, x);
 * }, function(err,result) {
 *     // result callback
 * });
 *
 * // descending order
 * async.sortBy([1,9,3,5], function(x, callback) {
 *     callback(null, x*-1);    //<- x*-1 instead of x, turns the order around
 * }, function(err,result) {
 *     // result callback
 * });
 */
function sortBy(coll, iteratee, callback) {
    var _iteratee = wrapAsync$1(iteratee);
    map(coll, function (x, callback) {
        _iteratee(x, function (err, criteria) {
            if (err) return callback(err);
            callback(null, { value: x, criteria: criteria });
        });
    }, function (err, results) {
        if (err) return callback(err);
        callback(null, arrayMap(results.sort(comparator), baseProperty('value')));
    });
 
    function comparator(left, right) {
        var a = left.criteria,
            b = right.criteria;
        return a < b ? -1 : a > b ? 1 : 0;
    }
}
 
/**
 * Sets a time limit on an asynchronous function. If the function does not call
 * its callback within the specified milliseconds, it will be called with a
 * timeout error. The code property for the error object will be `'ETIMEDOUT'`.
 *
 * @name timeout
 * @static
 * @memberOf module:Utils
 * @method
 * @category Util
 * @param {AsyncFunction} asyncFn - The async function to limit in time.
 * @param {number} milliseconds - The specified time limit.
 * @param {*} [info] - Any variable you want attached (`string`, `object`, etc)
 * to timeout Error for more information..
 * @returns {AsyncFunction} Returns a wrapped function that can be used with any
 * of the control flow functions.
 * Invoke this function with the same parameters as you would `asyncFunc`.
 * @example
 *
 * function myFunction(foo, callback) {
 *     doAsyncTask(foo, function(err, data) {
 *         // handle errors
 *         if (err) return callback(err);
 *
 *         // do some stuff ...
 *
 *         // return processed data
 *         return callback(null, data);
 *     });
 * }
 *
 * var wrapped = async.timeout(myFunction, 1000);
 *
 * // call `wrapped` as you would `myFunction`
 * wrapped({ bar: 'bar' }, function(err, data) {
 *     // if `myFunction` takes < 1000 ms to execute, `err`
 *     // and `data` will have their expected values
 *
 *     // else `err` will be an Error with the code 'ETIMEDOUT'
 * });
 */
function timeout(asyncFn, milliseconds, info) {
    var originalCallback, timer;
    var timedOut = false;
 
    function injectedCallback() {
        if (!timedOut) {
            originalCallback.apply(null, arguments);
            clearTimeout(timer);
        }
    }
 
    function timeoutCallback() {
        var name = asyncFn.name || 'anonymous';
        var error = new Error('Callback function "' + name + '" timed out.');
        error.code = 'ETIMEDOUT';
        if (info) {
            error.info = info;
        }
        timedOut = true;
        originalCallback(error);
    }
 
    var fn = wrapAsync$1(asyncFn);
 
    return initialParams(function (args, origCallback) {
        originalCallback = origCallback;
        // setup timer and call original function
        timer = setTimeout(timeoutCallback, milliseconds);
        fn.apply(null, args.concat(injectedCallback));
    });
}
 
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeCeil = Math.ceil;
var nativeMax$1 = Math.max;
 
/**
 * The base implementation of `_.range` and `_.rangeRight` which doesn't
 * coerce arguments.
 *
 * @private
 * @param {number} start The start of the range.
 * @param {number} end The end of the range.
 * @param {number} step The value to increment or decrement by.
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {Array} Returns the range of numbers.
 */
function baseRange(start, end, step, fromRight) {
  var index = -1,
      length = nativeMax$1(nativeCeil((end - start) / (step || 1)), 0),
      result = Array(length);
 
  while (length--) {
    result[fromRight ? length : ++index] = start;
    start += step;
  }
  return result;
}
 
/**
 * The same as [times]{@link module:ControlFlow.times} but runs a maximum of `limit` async operations at a
 * time.
 *
 * @name timesLimit
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.times]{@link module:ControlFlow.times}
 * @category Control Flow
 * @param {number} count - The number of times to run the function.
 * @param {number} limit - The maximum number of async operations at a time.
 * @param {AsyncFunction} iteratee - The async function to call `n` times.
 * Invoked with the iteration index and a callback: (n, next).
 * @param {Function} callback - see [async.map]{@link module:Collections.map}.
 */
function timeLimit(count, limit, iteratee, callback) {
  var _iteratee = wrapAsync$1(iteratee);
  mapLimit(baseRange(0, count, 1), limit, _iteratee, callback);
}
 
/**
 * Calls the `iteratee` function `n` times, and accumulates results in the same
 * manner you would use with [map]{@link module:Collections.map}.
 *
 * @name times
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.map]{@link module:Collections.map}
 * @category Control Flow
 * @param {number} n - The number of times to run the function.
 * @param {AsyncFunction} iteratee - The async function to call `n` times.
 * Invoked with the iteration index and a callback: (n, next).
 * @param {Function} callback - see {@link module:Collections.map}.
 * @example
 *
 * // Pretend this is some complicated async factory
 * var createUser = function(id, callback) {
 *     callback(null, {
 *         id: 'user' + id
 *     });
 * };
 *
 * // generate 5 users
 * async.times(5, function(n, next) {
 *     createUser(n, function(err, user) {
 *         next(err, user);
 *     });
 * }, function(err, users) {
 *     // we should now have 5 users
 * });
 */
var times = doLimit(timeLimit, Infinity);
 
/**
 * The same as [times]{@link module:ControlFlow.times} but runs only a single async operation at a time.
 *
 * @name timesSeries
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.times]{@link module:ControlFlow.times}
 * @category Control Flow
 * @param {number} n - The number of times to run the function.
 * @param {AsyncFunction} iteratee - The async function to call `n` times.
 * Invoked with the iteration index and a callback: (n, next).
 * @param {Function} callback - see {@link module:Collections.map}.
 */
var timesSeries = doLimit(timeLimit, 1);
 
/**
 * A relative of `reduce`.  Takes an Object or Array, and iterates over each
 * element in series, each step potentially mutating an `accumulator` value.
 * The type of the accumulator defaults to the type of collection passed in.
 *
 * @name transform
 * @static
 * @memberOf module:Collections
 * @method
 * @category Collection
 * @param {Array|Iterable|Object} coll - A collection to iterate over.
 * @param {*} [accumulator] - The initial state of the transform.  If omitted,
 * it will default to an empty Object or Array, depending on the type of `coll`
 * @param {AsyncFunction} iteratee - A function applied to each item in the
 * collection that potentially modifies the accumulator.
 * Invoked with (accumulator, item, key, callback).
 * @param {Function} [callback] - A callback which is called after all the
 * `iteratee` functions have finished. Result is the transformed accumulator.
 * Invoked with (err, result).
 * @example
 *
 * async.transform([1,2,3], function(acc, item, index, callback) {
 *     // pointless async:
 *     process.nextTick(function() {
 *         acc.push(item * 2)
 *         callback(null)
 *     });
 * }, function(err, result) {
 *     // result is now equal to [2, 4, 6]
 * });
 *
 * @example
 *
 * async.transform({a: 1, b: 2, c: 3}, function (obj, val, key, callback) {
 *     setImmediate(function () {
 *         obj[key] = val * 2;
 *         callback();
 *     })
 * }, function (err, result) {
 *     // result is equal to {a: 2, b: 4, c: 6}
 * })
 */
function transform(coll, accumulator, iteratee, callback) {
    if (arguments.length <= 3) {
        callback = iteratee;
        iteratee = accumulator;
        accumulator = isArray(coll) ? [] : {};
    }
    callback = once(callback || noop);
    var _iteratee = wrapAsync$1(iteratee);
 
    eachOf(coll, function (v, k, cb) {
        _iteratee(accumulator, v, k, cb);
    }, function (err) {
        callback(err, accumulator);
    });
}
 
/**
 * Undoes a [memoize]{@link module:Utils.memoize}d function, reverting it to the original,
 * unmemoized form. Handy for testing.
 *
 * @name unmemoize
 * @static
 * @memberOf module:Utils
 * @method
 * @see [async.memoize]{@link module:Utils.memoize}
 * @category Util
 * @param {AsyncFunction} fn - the memoized function
 * @returns {AsyncFunction} a function that calls the original unmemoized function
 */
function unmemoize(fn) {
    return function () {
        return (fn.unmemoized || fn).apply(null, arguments);
    };
}
 
/**
 * Repeatedly call `iteratee`, while `test` returns `true`. Calls `callback` when
 * stopped, or an error occurs.
 *
 * @name whilst
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {Function} test - synchronous truth test to perform before each
 * execution of `iteratee`. Invoked with ().
 * @param {AsyncFunction} iteratee - An async function which is called each time
 * `test` passes. Invoked with (callback).
 * @param {Function} [callback] - A callback which is called after the test
 * function has failed and repeated execution of `iteratee` has stopped. `callback`
 * will be passed an error and any arguments passed to the final `iteratee`'s
 * callback. Invoked with (err, [results]);
 * @returns undefined
 * @example
 *
 * var count = 0;
 * async.whilst(
 *     function() { return count < 5; },
 *     function(callback) {
 *         count++;
 *         setTimeout(function() {
 *             callback(null, count);
 *         }, 1000);
 *     },
 *     function (err, n) {
 *         // 5 seconds have passed, n = 5
 *     }
 * );
 */
function whilst(test, iteratee, callback) {
    callback = onlyOnce(callback || noop);
    var _iteratee = wrapAsync$1(iteratee);
    if (!test()) return callback(null);
    var next = rest(function (err, args) {
        if (err) return callback(err);
        if (test()) return _iteratee(next);
        callback.apply(null, [null].concat(args));
    });
    _iteratee(next);
}
 
/**
 * Repeatedly call `iteratee` until `test` returns `true`. Calls `callback` when
 * stopped, or an error occurs. `callback` will be passed an error and any
 * arguments passed to the final `iteratee`'s callback.
 *
 * The inverse of [whilst]{@link module:ControlFlow.whilst}.
 *
 * @name until
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @see [async.whilst]{@link module:ControlFlow.whilst}
 * @category Control Flow
 * @param {Function} test - synchronous truth test to perform before each
 * execution of `iteratee`. Invoked with ().
 * @param {AsyncFunction} iteratee - An async function which is called each time
 * `test` fails. Invoked with (callback).
 * @param {Function} [callback] - A callback which is called after the test
 * function has passed and repeated execution of `iteratee` has stopped. `callback`
 * will be passed an error and any arguments passed to the final `iteratee`'s
 * callback. Invoked with (err, [results]);
 */
function until(test, iteratee, callback) {
    whilst(function () {
        return !test.apply(this, arguments);
    }, iteratee, callback);
}
 
/**
 * Runs the `tasks` array of functions in series, each passing their results to
 * the next in the array. However, if any of the `tasks` pass an error to their
 * own callback, the next function is not executed, and the main `callback` is
 * immediately called with the error.
 *
 * @name waterfall
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {Array} tasks - An array of [async functions]{@link AsyncFunction}
 * to run.
 * Each function should complete with any number of `result` values.
 * The `result` values will be passed as arguments, in order, to the next task.
 * @param {Function} [callback] - An optional callback to run once all the
 * functions have completed. This will be passed the results of the last task's
 * callback. Invoked with (err, [results]).
 * @returns undefined
 * @example
 *
 * async.waterfall([
 *     function(callback) {
 *         callback(null, 'one', 'two');
 *     },
 *     function(arg1, arg2, callback) {
 *         // arg1 now equals 'one' and arg2 now equals 'two'
 *         callback(null, 'three');
 *     },
 *     function(arg1, callback) {
 *         // arg1 now equals 'three'
 *         callback(null, 'done');
 *     }
 * ], function (err, result) {
 *     // result now equals 'done'
 * });
 *
 * // Or, with named functions:
 * async.waterfall([
 *     myFirstFunction,
 *     mySecondFunction,
 *     myLastFunction,
 * ], function (err, result) {
 *     // result now equals 'done'
 * });
 * function myFirstFunction(callback) {
 *     callback(null, 'one', 'two');
 * }
 * function mySecondFunction(arg1, arg2, callback) {
 *     // arg1 now equals 'one' and arg2 now equals 'two'
 *     callback(null, 'three');
 * }
 * function myLastFunction(arg1, callback) {
 *     // arg1 now equals 'three'
 *     callback(null, 'done');
 * }
 */
var waterfall = function (tasks, callback) {
    callback = once(callback || noop);
    if (!isArray(tasks)) return callback(new Error('First argument to waterfall must be an array of functions'));
    if (!tasks.length) return callback();
    var taskIndex = 0;
 
    function nextTask(args) {
        if (taskIndex === tasks.length) {
            return callback.apply(null, [null].concat(args));
        }
 
        var taskCallback = onlyOnce(rest(function (err, args) {
            if (err) {
                return callback.apply(null, [err].concat(args));
            }
            nextTask(args);
        }));
 
        args.push(taskCallback);
 
        var task = wrapAsync$1(tasks[taskIndex++]);
        task.apply(null, args);
    }
 
    nextTask([]);
};
 
/**
 * An "async function" in the context of Async is an asynchronous function with
 * a variable number of parameters, with the final parameter being a callback.
 * (`function (arg1, arg2, ..., callback) {}`)
 * The final callback is of the form `callback(err, results...)`, which must be
 * called once the function is completed.  The callback should be called with a
 * Error as its first argument to signal that an error occurred.
 * Otherwise, if no error occurred, it should be called with `null` as the first
 * argument, and any additional `result` arguments that may apply, to signal
 * successful completion.
 * The callback must be called exactly once, ideally on a later tick of the
 * JavaScript event loop.
 *
 * This type of function is also referred to as a "Node-style async function",
 * or a "continuation passing-style function" (CPS). Most of the methods of this
 * library are themselves CPS/Node-style async functions, or functions that
 * return CPS/Node-style async functions.
 *
 * Wherever we accept a Node-style async function, we also directly accept an
 * [ES2017 `async` function]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function}.
 * In this case, the `async` function will not be passed a final callback
 * argument, and any thrown error will be used as the `err` argument of the
 * implicit callback, and the return value will be used as the `result` value.
 * (i.e. a `rejected` of the returned Promise becomes the `err` callback
 * argument, and a `resolved` value becomes the `result`.)
 *
 * Note, due to JavaScript limitations, we can only detect native `async`
 * functions and not transpilied implementations.
 * Your environment must have `async`/`await` support for this to work.
 * (e.g. Node > v7.6, or a recent version of a modern browser).
 * If you are using `async` functions through a transpiler (e.g. Babel), you
 * must still wrap the function with [asyncify]{@link module:Utils.asyncify},
 * because the `async function` will be compiled to an ordinary function that
 * returns a promise.
 *
 * @typedef {Function} AsyncFunction
 * @static
 */
 
/**
 * Async is a utility module which provides straight-forward, powerful functions
 * for working with asynchronous JavaScript. Although originally designed for
 * use with [Node.js](http://nodejs.org) and installable via
 * `npm install --save async`, it can also be used directly in the browser.
 * @module async
 * @see AsyncFunction
 */
 
/**
 * A collection of `async` functions for manipulating collections, such as
 * arrays and objects.
 * @module Collections
 */
 
/**
 * A collection of `async` functions for controlling the flow through a script.
 * @module ControlFlow
 */
 
/**
 * A collection of `async` utility functions.
 * @module Utils
 */
 
var index = {
  applyEach: applyEach,
  applyEachSeries: applyEachSeries,
  apply: apply$2,
  asyncify: asyncify,
  auto: auto,
  autoInject: autoInject,
  cargo: cargo,
  compose: compose,
  concat: concat,
  concatSeries: concatSeries,
  constant: constant,
  detect: detect,
  detectLimit: detectLimit,
  detectSeries: detectSeries,
  dir: dir,
  doDuring: doDuring,
  doUntil: doUntil,
  doWhilst: doWhilst,
  during: during,
  each: eachLimit,
  eachLimit: eachLimit$1,
  eachOf: eachOf,
  eachOfLimit: eachOfLimit,
  eachOfSeries: eachOfSeries,
  eachSeries: eachSeries,
  ensureAsync: ensureAsync,
  every: every,
  everyLimit: everyLimit,
  everySeries: everySeries,
  filter: filter,
  filterLimit: filterLimit,
  filterSeries: filterSeries,
  forever: forever,
  groupBy: groupBy,
  groupByLimit: groupByLimit,
  groupBySeries: groupBySeries,
  log: log,
  map: map,
  mapLimit: mapLimit,
  mapSeries: mapSeries,
  mapValues: mapValues,
  mapValuesLimit: mapValuesLimit,
  mapValuesSeries: mapValuesSeries,
  memoize: memoize,
  nextTick: nextTick,
  parallel: parallelLimit,
  parallelLimit: parallelLimit$1,
  priorityQueue: priorityQueue,
  queue: queue$1,
  race: race,
  reduce: reduce,
  reduceRight: reduceRight,
  reflect: reflect,
  reflectAll: reflectAll,
  reject: reject,
  rejectLimit: rejectLimit,
  rejectSeries: rejectSeries,
  retry: retry,
  retryable: retryable,
  seq: seq$1,
  series: series,
  setImmediate: setImmediate$1,
  some: some,
  someLimit: someLimit,
  someSeries: someSeries,
  sortBy: sortBy,
  timeout: timeout,
  times: times,
  timesLimit: timeLimit,
  timesSeries: timesSeries,
  transform: transform,
  unmemoize: unmemoize,
  until: until,
  waterfall: waterfall,
  whilst: whilst,
 
  // aliases
  all: every,
  any: some,
  forEach: eachLimit,
  forEachSeries: eachSeries,
  forEachLimit: eachLimit$1,
  forEachOf: eachOf,
  forEachOfSeries: eachOfSeries,
  forEachOfLimit: eachOfLimit,
  inject: reduce,
  foldl: reduce,
  foldr: reduceRight,
  select: filter,
  selectLimit: filterLimit,
  selectSeries: filterSeries,
  wrapSync: asyncify
};
 
exports['default'] = index;
exports.applyEach = applyEach;
exports.applyEachSeries = applyEachSeries;
exports.apply = apply$2;
exports.asyncify = asyncify;
exports.auto = auto;
exports.autoInject = autoInject;
exports.cargo = cargo;
exports.compose = compose;
exports.concat = concat;
exports.concatSeries = concatSeries;
exports.constant = constant;
exports.detect = detect;
exports.detectLimit = detectLimit;
exports.detectSeries = detectSeries;
exports.dir = dir;
exports.doDuring = doDuring;
exports.doUntil = doUntil;
exports.doWhilst = doWhilst;
exports.during = during;
exports.each = eachLimit;
exports.eachLimit = eachLimit$1;
exports.eachOf = eachOf;
exports.eachOfLimit = eachOfLimit;
exports.eachOfSeries = eachOfSeries;
exports.eachSeries = eachSeries;
exports.ensureAsync = ensureAsync;
exports.every = every;
exports.everyLimit = everyLimit;
exports.everySeries = everySeries;
exports.filter = filter;
exports.filterLimit = filterLimit;
exports.filterSeries = filterSeries;
exports.forever = forever;
exports.groupBy = groupBy;
exports.groupByLimit = groupByLimit;
exports.groupBySeries = groupBySeries;
exports.log = log;
exports.map = map;
exports.mapLimit = mapLimit;
exports.mapSeries = mapSeries;
exports.mapValues = mapValues;
exports.mapValuesLimit = mapValuesLimit;
exports.mapValuesSeries = mapValuesSeries;
exports.memoize = memoize;
exports.nextTick = nextTick;
exports.parallel = parallelLimit;
exports.parallelLimit = parallelLimit$1;
exports.priorityQueue = priorityQueue;
exports.queue = queue$1;
exports.race = race;
exports.reduce = reduce;
exports.reduceRight = reduceRight;
exports.reflect = reflect;
exports.reflectAll = reflectAll;
exports.reject = reject;
exports.rejectLimit = rejectLimit;
exports.rejectSeries = rejectSeries;
exports.retry = retry;
exports.retryable = retryable;
exports.seq = seq$1;
exports.series = series;
exports.setImmediate = setImmediate$1;
exports.some = some;
exports.someLimit = someLimit;
exports.someSeries = someSeries;
exports.sortBy = sortBy;
exports.timeout = timeout;
exports.times = times;
exports.timesLimit = timeLimit;
exports.timesSeries = timesSeries;
exports.transform = transform;
exports.unmemoize = unmemoize;
exports.until = until;
exports.waterfall = waterfall;
exports.whilst = whilst;
exports.all = every;
exports.allLimit = everyLimit;
exports.allSeries = everySeries;
exports.any = some;
exports.anyLimit = someLimit;
exports.anySeries = someSeries;
exports.find = detect;
exports.findLimit = detectLimit;
exports.findSeries = detectSeries;
exports.forEach = eachLimit;
exports.forEachSeries = eachSeries;
exports.forEachLimit = eachLimit$1;
exports.forEachOf = eachOf;
exports.forEachOfSeries = eachOfSeries;
exports.forEachOfLimit = eachOfLimit;
exports.inject = reduce;
exports.foldl = reduce;
exports.foldr = reduceRight;
exports.select = filter;
exports.selectLimit = filterLimit;
exports.selectSeries = filterSeries;
exports.wrapSync = asyncify;
 
Object.defineProperty(exports, '__esModule', { value: true });
 
})));
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/bl/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/bl/

Statements: 17.5% (21 / 120)      Branches: 0% (0 / 61)      Functions: 10.53% (2 / 19)      Lines: 17.5% (21 / 120)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/bl/
File Statements Branches Functions Lines
bl.js 17.5% (21 / 120) 0% (0 / 61) 10.53% (2 / 19) 17.5% (21 / 120)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/bl/bl.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/bl/bl.js

Statements: 17.5% (21 / 120)      Branches: 0% (0 / 61)      Functions: 10.53% (2 / 19)      Lines: 17.5% (21 / 120)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 2451       1                                                             1     1                     1                                                   1               1                   1                   1         1         1                                                                                                                             1       1                               1                     1             1 1                                 1 14 14               1    
var DuplexStream = require('readable-stream/duplex')
  , util         = require('util')
 
 
function BufferList (callback) {
  if (!(this instanceof BufferList))
    return new BufferList(callback)
 
  this._bufs  = []
  this.length = 0
 
  if (typeof callback == 'function') {
    this._callback = callback
 
    var piper = function piper (err) {
      if (this._callback) {
        this._callback(err)
        this._callback = null
      }
    }.bind(this)
 
    this.on('pipe', function onPipe (src) {
      src.on('error', piper)
    })
    this.on('unpipe', function onUnpipe (src) {
      src.removeListener('error', piper)
    })
  } else {
    this.append(callback)
  }
 
  DuplexStream.call(this)
}
 
 
util.inherits(BufferList, DuplexStream)
 
 
BufferList.prototype._offset = function _offset (offset) {
  var tot = 0, i = 0, _t
  for (; i < this._bufs.length; i++) {
    _t = tot + this._bufs[i].length
    if (offset < _t)
      return [ i, offset - tot ]
    tot = _t
  }
}
 
 
BufferList.prototype.append = function append (buf) {
  var i = 0
    , newBuf
 
  if (Array.isArray(buf)) {
    for (; i < buf.length; i++)
      this.append(buf[i])
  } else if (buf instanceof BufferList) {
    // unwrap argument into individual BufferLists
    for (; i < buf._bufs.length; i++)
      this.append(buf._bufs[i])
  } else if (buf != null) {
    // coerce number arguments to strings, since Buffer(number) does
    // uninitialized memory allocation
    if (typeof buf == 'number')
      buf = buf.toString()
 
    newBuf = Buffer.isBuffer(buf) ? buf : new Buffer(buf)
    this._bufs.push(newBuf)
    this.length += newBuf.length
  }
 
  return this
}
 
 
BufferList.prototype._write = function _write (buf, encoding, callback) {
  this.append(buf)
 
  if (typeof callback == 'function')
    callback()
}
 
 
BufferList.prototype._read = function _read (size) {
  if (!this.length)
    return this.push(null)
 
  size = Math.min(size, this.length)
  this.push(this.slice(0, size))
  this.consume(size)
}
 
 
BufferList.prototype.end = function end (chunk) {
  DuplexStream.prototype.end.call(this, chunk)
 
  if (this._callback) {
    this._callback(null, this.slice())
    this._callback = null
  }
}
 
 
BufferList.prototype.get = function get (index) {
  return this.slice(index, index + 1)[0]
}
 
 
BufferList.prototype.slice = function slice (start, end) {
  return this.copy(null, 0, start, end)
}
 
 
BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) {
  if (typeof srcStart != 'number' || srcStart < 0)
    srcStart = 0
  if (typeof srcEnd != 'number' || srcEnd > this.length)
    srcEnd = this.length
  if (srcStart >= this.length)
    return dst || new Buffer(0)
  if (srcEnd <= 0)
    return dst || new Buffer(0)
 
  var copy   = !!dst
    , off    = this._offset(srcStart)
    , len    = srcEnd - srcStart
    , bytes  = len
    , bufoff = (copy && dstStart) || 0
    , start  = off[1]
    , l
    , i
 
  // copy/slice everything
  if (srcStart === 0 && srcEnd == this.length) {
    if (!copy) // slice, just return a full concat
      return Buffer.concat(this._bufs)
 
    // copy, need to copy individual buffers
    for (i = 0; i < this._bufs.length; i++) {
      this._bufs[i].copy(dst, bufoff)
      bufoff += this._bufs[i].length
    }
 
    return dst
  }
 
  // easy, cheap case where it's a subset of one of the buffers
  if (bytes <= this._bufs[off[0]].length - start) {
    return copy
      ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes)
      : this._bufs[off[0]].slice(start, start + bytes)
  }
 
  if (!copy) // a slice, we need something to copy in to
    dst = new Buffer(len)
 
  for (i = off[0]; i < this._bufs.length; i++) {
    l = this._bufs[i].length - start
 
    if (bytes > l) {
      this._bufs[i].copy(dst, bufoff, start)
    } else {
      this._bufs[i].copy(dst, bufoff, start, start + bytes)
      break
    }
 
    bufoff += l
    bytes -= l
 
    if (start)
      start = 0
  }
 
  return dst
}
 
BufferList.prototype.toString = function toString (encoding, start, end) {
  return this.slice(start, end).toString(encoding)
}
 
BufferList.prototype.consume = function consume (bytes) {
  while (this._bufs.length) {
    if (bytes >= this._bufs[0].length) {
      bytes -= this._bufs[0].length
      this.length -= this._bufs[0].length
      this._bufs.shift()
    } else {
      this._bufs[0] = this._bufs[0].slice(bytes)
      this.length -= bytes
      break
    }
  }
  return this
}
 
 
BufferList.prototype.duplicate = function duplicate () {
  var i = 0
    , copy = new BufferList()
 
  for (; i < this._bufs.length; i++)
    copy.append(this._bufs[i])
 
  return copy
}
 
 
BufferList.prototype.destroy = function destroy () {
  this._bufs.length = 0
  this.length = 0
  this.push(null)
}
 
 
;(function () {
  var methods = {
      'readDoubleBE' : 8
    , 'readDoubleLE' : 8
    , 'readFloatBE'  : 4
    , 'readFloatLE'  : 4
    , 'readInt32BE'  : 4
    , 'readInt32LE'  : 4
    , 'readUInt32BE' : 4
    , 'readUInt32LE' : 4
    , 'readInt16BE'  : 2
    , 'readInt16LE'  : 2
    , 'readUInt16BE' : 2
    , 'readUInt16LE' : 2
    , 'readInt8'     : 1
    , 'readUInt8'    : 1
  }
 
  for (var m in methods) {
    (function (m) {
      BufferList.prototype[m] = function (offset) {
        return this.slice(offset, offset + methods[m])[m](0)
      }
    }(m))
  }
}())
 
 
module.exports = BufferList
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/caseless/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/caseless/

Statements: 15.09% (8 / 53)      Branches: 0% (0 / 22)      Functions: 0% (0 / 13)      Lines: 17.39% (8 / 46)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/caseless/
File Statements Branches Functions Lines
index.js 15.09% (8 / 53) 0% (0 / 22) 0% (0 / 13) 17.39% (8 / 46)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/caseless/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/caseless/index.js

Statements: 15.09% (8 / 53)      Branches: 0% (0 / 22)      Functions: 0% (0 / 13)      Lines: 17.39% (8 / 46)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 681     1                           1                 1                   1           1         1 1                                      
function Caseless (dict) {
  this.dict = dict || {}
}
Caseless.prototype.set = function (name, value, clobber) {
  if (typeof name === 'object') {
    for (var i in name) {
      this.set(i, name[i], value)
    }
  } else {
    if (typeof clobber === 'undefined') clobber = true
    var has = this.has(name)
 
    if (!clobber && has) this.dict[has] = this.dict[has] + ',' + value
    else this.dict[has || name] = value
    return has
  }
}
Caseless.prototype.has = function (name) {
  var keys = Object.keys(this.dict)
    , name = name.toLowerCase()
    ;
  for (var i=0;i<keys.length;i++) {
    if (keys[i].toLowerCase() === name) return keys[i]
  }
  return false
}
Caseless.prototype.get = function (name) {
  name = name.toLowerCase()
  var result, _key
  var headers = this.dict
  Object.keys(headers).forEach(function (key) {
    _key = key.toLowerCase()
    if (name === _key) result = headers[key]
  })
  return result
}
Caseless.prototype.swap = function (name) {
  var has = this.has(name)
  if (!has) throw new Error('There is no header than matches "'+name+'"')
  this.dict[name] = this.dict[has]
  delete this.dict[has]
}
Caseless.prototype.del = function (name) {
  var has = this.has(name)
  return delete this.dict[has || name]
}
 
module.exports = function (dict) {return new Caseless(dict)}
module.exports.httpify = function (resp, headers) {
  var c = new Caseless(headers)
  resp.setHeader = function (key, value, clobber) {
    if (typeof value === 'undefined') return
    return c.set(key, value, clobber)
  }
  resp.hasHeader = function (key) {
    return c.has(key)
  }
  resp.getHeader = function (key) {
    return c.get(key)
  }
  resp.removeHeader = function (key) {
    return c.del(key)
  }
  resp.headers = c.dict
  return c
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/

Statements: 16.32% (31 / 190)      Branches: 0% (0 / 121)      Functions: 0% (0 / 25)      Lines: 16.32% (31 / 190)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/
File Statements Branches Functions Lines
form_data.js 16.13% (30 / 186) 0% (0 / 119) 0% (0 / 23) 16.13% (30 / 186)
populate.js 25% (1 / 4) 0% (0 / 2) 0% (0 / 2) 25% (1 / 4)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/form_data.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/form_data.js

Statements: 16.13% (30 / 186)      Branches: 0% (0 / 119)      Functions: 0% (0 / 23)      Lines: 16.13% (30 / 186)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 4391 1 1 1 1 1 1 1 1 1     1     1                 1                       1 1   1                                                                     1                                                                                                                                                                           1                                                                                             1                                         1                                                                 1                         1       1                               1                     1               1                           1                                       1                                                   1                                                                                                                 1                
var CombinedStream = require('combined-stream');
var util = require('util');
var path = require('path');
var http = require('http');
var https = require('https');
var parseUrl = require('url').parse;
var fs = require('fs');
var mime = require('mime-types');
var async = require('async');
var populate = require('./populate.js');
 
// Public API
module.exports = FormData;
 
// make it a Stream
util.inherits(FormData, CombinedStream);
 
/**
 * Create readable "multipart/form-data" streams.
 * Can be used to submit forms
 * and file uploads to other web applications.
 *
 * @constructor
 */
function FormData() {
  if (!(this instanceof FormData)) {
    throw new TypeError('Failed to construct FormData: Please use the _new_ operator, this object constructor cannot be called as a function.');
  }
 
  this._overheadLength = 0;
  this._valueLength = 0;
  this._lengthRetrievers = [];
 
  CombinedStream.call(this);
}
 
FormData.LINE_BREAK = '\r\n';
FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream';
 
FormData.prototype.append = function(field, value, options) {
 
  options = options || {};
 
  // allow filename as single option
  if (typeof options == 'string') {
    options = {filename: options};
  }
 
  var append = CombinedStream.prototype.append.bind(this);
 
  // all that streamy business can't handle numbers
  if (typeof value == 'number') {
    value = '' + value;
  }
 
  // https://github.com/felixge/node-form-data/issues/38
  if (util.isArray(value)) {
    // Please convert your array into string
    // the way web server expects it
    this._error(new Error('Arrays are not supported.'));
    return;
  }
 
  var header = this._multiPartHeader(field, value, options);
  var footer = this._multiPartFooter();
 
  append(header);
  append(value);
  append(footer);
 
  // pass along options.knownLength
  this._trackLength(header, value, options);
};
 
FormData.prototype._trackLength = function(header, value, options) {
  var valueLength = 0;
 
  // used w/ getLengthSync(), when length is known.
  // e.g. for streaming directly from a remote server,
  // w/ a known file a size, and not wanting to wait for
  // incoming file to finish to get its size.
  if (options.knownLength != null) {
    valueLength += +options.knownLength;
  } else if (Buffer.isBuffer(value)) {
    valueLength = value.length;
  } else if (typeof value === 'string') {
    valueLength = Buffer.byteLength(value);
  }
 
  this._valueLength += valueLength;
 
  // @check why add CRLF? does this account for custom/multiple CRLFs?
  this._overheadLength +=
    Buffer.byteLength(header) +
    FormData.LINE_BREAK.length;
 
  // empty or either doesn't have path or not an http response
  if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) )) {
    return;
  }
 
  // no need to bother with the length
  if (!options.knownLength) {
    this._lengthRetrievers.push(function(next) {
 
      if (value.hasOwnProperty('fd')) {
 
        // take read range into a account
        // `end` = Infinity –> read file till the end
        //
        // TODO: Looks like there is bug in Node fs.createReadStream
        // it doesn't respect `end` options without `start` options
        // Fix it when node fixes it.
        // https://github.com/joyent/node/issues/7819
        if (value.end != undefined && value.end != Infinity && value.start != undefined) {
 
          // when end specified
          // no need to calculate range
          // inclusive, starts with 0
          next(null, value.end + 1 - (value.start ? value.start : 0));
 
        // not that fast snoopy
        } else {
          // still need to fetch file size from fs
          fs.stat(value.path, function(err, stat) {
 
            var fileSize;
 
            if (err) {
              next(err);
              return;
            }
 
            // update final size based on the range options
            fileSize = stat.size - (value.start ? value.start : 0);
            next(null, fileSize);
          });
        }
 
      // or http response
      } else if (value.hasOwnProperty('httpVersion')) {
        next(null, +value.headers['content-length']);
 
      // or request stream http://github.com/mikeal/request
      } else if (value.hasOwnProperty('httpModule')) {
        // wait till response come back
        value.on('response', function(response) {
          value.pause();
          next(null, +response.headers['content-length']);
        });
        value.resume();
 
      // something else
      } else {
        next('Unknown stream');
      }
    });
  }
};
 
FormData.prototype._multiPartHeader = function(field, value, options) {
  // custom header specified (as string)?
  // it becomes responsible for boundary
  // (e.g. to handle extra CRLFs on .NET servers)
  if (typeof options.header == 'string') {
    return options.header;
  }
 
  var contentDisposition = this._getContentDisposition(value, options);
  var contentType = this._getContentType(value, options);
 
  var contents = '';
  var headers  = {
    // add custom disposition as third element or keep it two elements if not
    'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []),
    // if no content type. allow it to be empty array
    'Content-Type': [].concat(contentType || [])
  };
 
  // allow custom headers.
  if (typeof options.header == 'object') {
    populate(headers, options.header);
  }
 
  var header;
  for (var prop in headers) {
    header = headers[prop];
 
    // skip nullish headers.
    if (header == null) {
      continue;
    }
 
    // convert all headers to arrays.
    if (!Array.isArray(header)) {
      header = [header];
    }
 
    // add non-empty headers.
    if (header.length) {
      contents += prop + ': ' + header.join('; ') + FormData.LINE_BREAK;
    }
  }
 
  return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK;
};
 
FormData.prototype._getContentDisposition = function(value, options) {
 
  var contentDisposition;
 
  // custom filename takes precedence
  // fs- and request- streams have path property
  // formidable and the browser add a name property.
  var filename = options.filename || value.name || value.path;
 
  // or try http response
  if (!filename && value.readable && value.hasOwnProperty('httpVersion')) {
    filename = value.client._httpMessage.path;
  }
 
  if (filename) {
    contentDisposition = 'filename="' + path.basename(filename) + '"';
  }
 
  return contentDisposition;
};
 
FormData.prototype._getContentType = function(value, options) {
 
  // use custom content-type above all
  var contentType = options.contentType;
 
  // or try `name` from formidable, browser
  if (!contentType && value.name) {
    contentType = mime.lookup(value.name);
  }
 
  // or try `path` from fs-, request- streams
  if (!contentType && value.path) {
    contentType = mime.lookup(value.path);
  }
 
  // or if it's http-reponse
  if (!contentType && value.readable && value.hasOwnProperty('httpVersion')) {
    contentType = value.headers['content-type'];
  }
 
  // or guess it from the filename
  if (!contentType && options.filename) {
    contentType = mime.lookup(options.filename);
  }
 
  // fallback to the default content type if `value` is not simple value
  if (!contentType && typeof value == 'object') {
    contentType = FormData.DEFAULT_CONTENT_TYPE;
  }
 
  return contentType;
};
 
FormData.prototype._multiPartFooter = function() {
  return function(next) {
    var footer = FormData.LINE_BREAK;
 
    var lastPart = (this._streams.length === 0);
    if (lastPart) {
      footer += this._lastBoundary();
    }
 
    next(footer);
  }.bind(this);
};
 
FormData.prototype._lastBoundary = function() {
  return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK;
};
 
FormData.prototype.getHeaders = function(userHeaders) {
  var header;
  var formHeaders = {
    'content-type': 'multipart/form-data; boundary=' + this.getBoundary()
  };
 
  for (header in userHeaders) {
    if (userHeaders.hasOwnProperty(header)) {
      formHeaders[header.toLowerCase()] = userHeaders[header];
    }
  }
 
  return formHeaders;
};
 
// TODO: Looks like unused function
FormData.prototype.getCustomHeaders = function(contentType) {
  contentType = contentType ? contentType : 'multipart/form-data';
 
  var formHeaders = {
    'content-type': contentType + '; boundary=' + this.getBoundary(),
    'content-length': this.getLengthSync()
  };
 
  return formHeaders;
};
 
FormData.prototype.getBoundary = function() {
  if (!this._boundary) {
    this._generateBoundary();
  }
 
  return this._boundary;
};
 
FormData.prototype._generateBoundary = function() {
  // This generates a 50 character boundary similar to those used by Firefox.
  // They are optimized for boyer-moore parsing.
  var boundary = '--------------------------';
  for (var i = 0; i < 24; i++) {
    boundary += Math.floor(Math.random() * 10).toString(16);
  }
 
  this._boundary = boundary;
};
 
// Note: getLengthSync DOESN'T calculate streams length
// As workaround one can calculate file size manually
// and add it as knownLength option
FormData.prototype.getLengthSync = function() {
  var knownLength = this._overheadLength + this._valueLength;
 
  // Don't get confused, there are 3 "internal" streams for each keyval pair
  // so it basically checks if there is any value added to the form
  if (this._streams.length) {
    knownLength += this._lastBoundary().length;
  }
 
  // https://github.com/form-data/form-data/issues/40
  if (this._lengthRetrievers.length) {
    // Some async length retrievers are present
    // therefore synchronous length calculation is false.
    // Please use getLength(callback) to get proper length
    this._error(new Error('Cannot calculate proper length in synchronous way.'));
  }
 
  return knownLength;
};
 
FormData.prototype.getLength = function(cb) {
  var knownLength = this._overheadLength + this._valueLength;
 
  if (this._streams.length) {
    knownLength += this._lastBoundary().length;
  }
 
  if (!this._lengthRetrievers.length) {
    process.nextTick(cb.bind(this, null, knownLength));
    return;
  }
 
  async.parallel(this._lengthRetrievers, function(err, values) {
    if (err) {
      cb(err);
      return;
    }
 
    values.forEach(function(length) {
      knownLength += length;
    });
 
    cb(null, knownLength);
  });
};
 
FormData.prototype.submit = function(params, cb) {
  var request
    , options
    , defaults = {method: 'post'}
    ;
 
  // parse provided url if it's string
  // or treat it as options object
  if (typeof params == 'string') {
 
    params = parseUrl(params);
    options = populate({
      port: params.port,
      path: params.pathname,
      host: params.hostname
    }, defaults);
 
  // use custom params
  } else {
 
    options = populate(params, defaults);
    // if no port provided use default one
    if (!options.port) {
      options.port = options.protocol == 'https:' ? 443 : 80;
    }
  }
 
  // put that good code in getHeaders to some use
  options.headers = this.getHeaders(params.headers);
 
  // https if specified, fallback to http in any other case
  if (options.protocol == 'https:') {
    request = https.request(options);
  } else {
    request = http.request(options);
  }
 
  // get content length and fire away
  this.getLength(function(err, length) {
    if (err) {
      this._error(err);
      return;
    }
 
    // add content length
    request.setHeader('Content-Length', length);
 
    this.pipe(request);
    if (cb) {
      request.on('error', cb);
      request.on('response', cb.bind(this, null));
    }
  }.bind(this));
 
  return request;
};
 
FormData.prototype._error = function(err) {
  if (!this.error) {
    this.error = err;
    this.pause();
    this.emit('error', err);
  }
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/populate.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/form-data/lib/populate.js

Statements: 25% (1 / 4)      Branches: 0% (0 / 2)      Functions: 0% (0 / 2)      Lines: 25% (1 / 4)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12  1                    
// populates missing values
module.exports = function(dst, src) {
 
  Object.keys(src).forEach(function(prop)
  {
    dst[prop] = dst[prop] || src[prop];
  });
 
  return dst;
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/

Statements: 55.56% (15 / 27)      Branches: 0% (0 / 8)      Functions: 28.57% (2 / 7)      Lines: 55.56% (15 / 27)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/
File Statements Branches Functions Lines
error.js 60% (3 / 5) 100% (0 / 0) 0% (0 / 1) 60% (3 / 5)
index.js 72.73% (8 / 11) 0% (0 / 2) 40% (2 / 5) 72.73% (8 / 11)
runner.js 36.36% (4 / 11) 0% (0 / 6) 0% (0 / 1) 36.36% (4 / 11)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/error.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/error.js

Statements: 60% (3 / 5)      Branches: 100% (0 / 0)      Functions: 0% (0 / 1)      Lines: 60% (3 / 5)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12    1         1   1    
'use strict'
 
function ValidationError (errors) {
  this.name = 'ValidationError'
  this.errors = errors
}
 
ValidationError.prototype = Error.prototype
 
module.exports = ValidationError
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/index.js

Statements: 72.73% (8 / 11)      Branches: 0% (0 / 2)      Functions: 40% (2 / 5)      Lines: 72.73% (8 / 11)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24    1 1 1   1 16                 1     1 15      
'use strict'
 
var Promise = require('pinkie-promise')
var runner = require('./runner')
var schemas = require('./schemas')
 
var promisify = function (schema) {
  return function (data) {
    return new Promise(function (resolve, reject) {
      runner(schema, data, function (err, valid) {
        return err === null ? resolve(data) : reject(err)
      })
    })
  }
}
 
module.exports = promisify(schemas.har)
 
// utility methods for all parts of the schema
Object.keys(schemas).map(function (name) {
  module.exports[name] = promisify(schemas[name])
})
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/runner.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/runner.js

Statements: 36.36% (4 / 11)      Branches: 0% (0 / 6)      Functions: 0% (0 / 1)      Lines: 36.36% (4 / 11)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31    1 1 1   1                                                
'use strict'
 
var schemas = require('./schemas')
var ValidationError = require('./error')
var validator = require('is-my-json-valid')
 
module.exports = function (schema, data, cb) {
  // default value
  var valid = false
 
  // validator config
  var validate = validator(schema, {
    greedy: true,
    verbose: true,
    schemas: schemas
  })
 
  // execute is-my-json-valid
  if (data !== undefined) {
    valid = validate(data)
  }
 
  // callback?
  if (typeof cb === 'function') {
    return cb(validate.errors ? new ValidationError(validate.errors) : null, valid)
  }
 
  return valid
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/schemas/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/schemas/

Statements: 100% (21 / 21)      Branches: 100% (0 / 0)      Functions: 100% (0 / 0)      Lines: 100% (21 / 21)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/schemas/
File Statements Branches Functions Lines
index.js 100% (21 / 21) 100% (0 / 0) 100% (0 / 0) 100% (21 / 21)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/schemas/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/har-validator/lib/schemas/index.js

Statements: 100% (21 / 21)      Branches: 100% (0 / 0)      Functions: 100% (0 / 0)      Lines: 100% (21 / 21)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51    1                                       1 1   1   1 1 1 1   1 1 1   1 1 1 1   1 1 1 1   1   1    
'use strict'
 
var schemas = {
  cache: require('./cache.json'),
  cacheEntry: require('./cacheEntry.json'),
  content: require('./content.json'),
  cookie: require('./cookie.json'),
  creator: require('./creator.json'),
  entry: require('./entry.json'),
  har: require('./har.json'),
  log: require('./log.json'),
  page: require('./page.json'),
  pageTimings: require('./pageTimings.json'),
  postData: require('./postData.json'),
  record: require('./record.json'),
  request: require('./request.json'),
  response: require('./response.json'),
  timings: require('./timings.json')
}
 
// is-my-json-valid does not provide meaningful error messages for external schemas
// this is a workaround
schemas.cache.properties.beforeRequest = schemas.cacheEntry
schemas.cache.properties.afterRequest = schemas.cacheEntry
 
schemas.page.properties.pageTimings = schemas.pageTimings
 
schemas.request.properties.cookies.items = schemas.cookie
schemas.request.properties.headers.items = schemas.record
schemas.request.properties.queryString.items = schemas.record
schemas.request.properties.postData = schemas.postData
 
schemas.response.properties.cookies.items = schemas.cookie
schemas.response.properties.headers.items = schemas.record
schemas.response.properties.content = schemas.content
 
schemas.entry.properties.request = schemas.request
schemas.entry.properties.response = schemas.response
schemas.entry.properties.cache = schemas.cache
schemas.entry.properties.timings = schemas.timings
 
schemas.log.properties.creator = schemas.creator
schemas.log.properties.browser = schemas.creator
schemas.log.properties.pages.items = schemas.page
schemas.log.properties.entries.items = schemas.entry
 
schemas.har.properties.log = schemas.log
 
module.exports = schemas
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/node-uuid/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/node-uuid/

Statements: 31.67% (38 / 120)      Branches: 10.34% (9 / 87)      Functions: 23.08% (3 / 13)      Lines: 31.62% (37 / 117)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/node-uuid/
File Statements Branches Functions Lines
uuid.js 31.67% (38 / 120) 10.34% (9 / 87) 23.08% (3 / 13) 31.62% (37 / 117)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/node-uuid/uuid.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/node-uuid/uuid.js

Statements: 31.67% (38 / 120)      Branches: 10.34% (9 / 87)      Functions: 23.08% (3 / 13)      Lines: 31.62% (37 / 117)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274            1           1   1                                                                           1       1 1 1 2 1         1     1       1     1 1 1 256 256       1                                     1                                   1     1           1     1     1                                                                                                                                                           1                                                     1 1 1 1 1 1 1 1 1 1   1   1                                        
//     uuid.js
//
//     Copyright (c) 2010-2012 Robert Kieffer
//     MIT License - http://opensource.org/licenses/mit-license.php
 
/*global window, require, define */
(function(_window) {
  'use strict';
 
  // Unique ID creation requires a high quality random # generator.  We feature
  // detect to determine the best RNG source, normalizing to a function that
  // returns 128-bits of randomness, since that's what's usually required
  var _rng, _mathRNG, _nodeRNG, _whatwgRNG, _previousRoot;
 
  function setupBrowser() {
    // Allow for MSIE11 msCrypto
    var _crypto = _window.crypto || _window.msCrypto;
 
    if (!_rng && _crypto && _crypto.getRandomValues) {
      // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
      //
      // Moderately fast, high quality
      try {
        var _rnds8 = new Uint8Array(16);
        _whatwgRNG = _rng = function whatwgRNG() {
          _crypto.getRandomValues(_rnds8);
          return _rnds8;
        };
        _rng();
      } catch(e) {}
    }
 
    if (!_rng) {
      // Math.random()-based (RNG)
      //
      // If all else fails, use Math.random().  It's fast, but is of unspecified
      // quality.
      var  _rnds = new Array(16);
      _mathRNG = _rng = function() {
        for (var i = 0, r; i < 16; i++) {
          if ((i & 0x03) === 0) { r = Math.random() * 0x100000000; }
          _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
        }
 
        return _rnds;
      };
      if ('undefined' !== typeof console && console.warn) {
        console.warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()");
      }
    }
  }
 
  function setupNode() {
    // Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html
    //
    // Moderately fast, high quality
    Eif ('function' === typeof require) {
      try {
        var _rb = require('crypto').randomBytes;
        _nodeRNG = _rng = _rb && function() {return _rb(16);};
        _rng();
      } catch(e) {}
    }
  }
 
  Iif (_window) {
    setupBrowser();
  } else {
    setupNode();
  }
 
  // Buffer class to use
  var BufferClass = ('function' === typeof Buffer) ? Buffer : Array;
 
  // Maps for number <-> hex string conversion
  var _byteToHex = [];
  var _hexToByte = {};
  for (var i = 0; i < 256; i++) {
    _byteToHex[i] = (i + 0x100).toString(16).substr(1);
    _hexToByte[_byteToHex[i]] = i;
  }
 
  // **`parse()` - Parse a UUID into it's component bytes**
  function parse(s, buf, offset) {
    var i = (buf && offset) || 0, ii = 0;
 
    buf = buf || [];
    s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {
      if (ii < 16) { // Don't overflow!
        buf[i + ii++] = _hexToByte[oct];
      }
    });
 
    // Zero out remaining bytes if string was short
    while (ii < 16) {
      buf[i + ii++] = 0;
    }
 
    return buf;
  }
 
  // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
  function unparse(buf, offset) {
    var i = offset || 0, bth = _byteToHex;
    return  bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] + '-' +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]] +
            bth[buf[i++]] + bth[buf[i++]];
  }
 
  // **`v1()` - Generate time-based UUID**
  //
  // Inspired by https://github.com/LiosK/UUID.js
  // and http://docs.python.org/library/uuid.html
 
  // random #'s we need to init node and clockseq
  var _seedBytes = _rng();
 
  // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
  var _nodeId = [
    _seedBytes[0] | 0x01,
    _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]
  ];
 
  // Per 4.2.2, randomize (14 bit) clockseq
  var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
 
  // Previous uuid creation time
  var _lastMSecs = 0, _lastNSecs = 0;
 
  // See https://github.com/broofa/node-uuid for API details
  function v1(options, buf, offset) {
    var i = buf && offset || 0;
    var b = buf || [];
 
    options = options || {};
 
    var clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
 
    // UUID timestamps are 100 nano-second units since the Gregorian epoch,
    // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so
    // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
    // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
    var msecs = (options.msecs != null) ? options.msecs : new Date().getTime();
 
    // Per 4.2.1.2, use count of uuid's generated during the current clock
    // cycle to simulate higher resolution clock
    var nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1;
 
    // Time since last uuid creation (in msecs)
    var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
 
    // Per 4.2.1.2, Bump clockseq on clock regression
    if (dt < 0 && options.clockseq == null) {
      clockseq = clockseq + 1 & 0x3fff;
    }
 
    // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
    // time interval
    if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
      nsecs = 0;
    }
 
    // Per 4.2.1.2 Throw error if too many uuids are requested
    if (nsecs >= 10000) {
      throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
    }
 
    _lastMSecs = msecs;
    _lastNSecs = nsecs;
    _clockseq = clockseq;
 
    // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
    msecs += 12219292800000;
 
    // `time_low`
    var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
    b[i++] = tl >>> 24 & 0xff;
    b[i++] = tl >>> 16 & 0xff;
    b[i++] = tl >>> 8 & 0xff;
    b[i++] = tl & 0xff;
 
    // `time_mid`
    var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
    b[i++] = tmh >>> 8 & 0xff;
    b[i++] = tmh & 0xff;
 
    // `time_high_and_version`
    b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
    b[i++] = tmh >>> 16 & 0xff;
 
    // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
    b[i++] = clockseq >>> 8 | 0x80;
 
    // `clock_seq_low`
    b[i++] = clockseq & 0xff;
 
    // `node`
    var node = options.node || _nodeId;
    for (var n = 0; n < 6; n++) {
      b[i + n] = node[n];
    }
 
    return buf ? buf : unparse(b);
  }
 
  // **`v4()` - Generate random UUID**
 
  // See https://github.com/broofa/node-uuid for API details
  function v4(options, buf, offset) {
    // Deprecated - 'format' argument, as supported in v1.2
    var i = buf && offset || 0;
 
    if (typeof(options) === 'string') {
      buf = (options === 'binary') ? new BufferClass(16) : null;
      options = null;
    }
    options = options || {};
 
    var rnds = options.random || (options.rng || _rng)();
 
    // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
    rnds[6] = (rnds[6] & 0x0f) | 0x40;
    rnds[8] = (rnds[8] & 0x3f) | 0x80;
 
    // Copy bytes to buffer, if provided
    if (buf) {
      for (var ii = 0; ii < 16; ii++) {
        buf[i + ii] = rnds[ii];
      }
    }
 
    return buf || unparse(rnds);
  }
 
  // Export public API
  var uuid = v4;
  uuid.v1 = v1;
  uuid.v4 = v4;
  uuid.parse = parse;
  uuid.unparse = unparse;
  uuid.BufferClass = BufferClass;
  uuid._rng = _rng;
  uuid._mathRNG = _mathRNG;
  uuid._nodeRNG = _nodeRNG;
  uuid._whatwgRNG = _whatwgRNG;
 
  Eif (('undefined' !== typeof module) && module.exports) {
    // Publish as node.js module
    module.exports = uuid;
  } else if (typeof define === 'function' && define.amd) {
    // Publish as AMD module
    define(function() {return uuid;});
 
 
  } else {
    // Publish as global (in browsers)
    _previousRoot = _window.uuid;
 
    // **`noConflict()` - (browser only) to reset global 'uuid' var**
    uuid.noConflict = function() {
      _window.uuid = _previousRoot;
      return uuid;
    };
 
    _window.uuid = uuid;
  }
})('undefined' !== typeof window ? window : null);
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/

Statements: 11.24% (28 / 249)      Branches: 0.83% (2 / 241)      Functions: 5.56% (1 / 18)      Lines: 11.24% (28 / 249)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/
File Statements Branches Functions Lines
index.js 100% (3 / 3) 100% (0 / 0) 100% (0 / 0) 100% (3 / 3)
parse.js 8.43% (7 / 83) 0% (0 / 90) 0% (0 / 4) 8.43% (7 / 83)
stringify.js 6.67% (5 / 75) 0% (0 / 77) 0% (0 / 5) 6.67% (5 / 75)
utils.js 14.77% (13 / 88) 2.7% (2 / 74) 11.11% (1 / 9) 14.77% (13 / 88)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/index.js

Statements: 100% (3 / 3)      Branches: 100% (0 / 0)      Functions: 100% (0 / 0)      Lines: 100% (3 / 3)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11    1 1   1          
'use strict';
 
var Stringify = require('./stringify');
var Parse = require('./parse');
 
module.exports = {
    stringify: Stringify,
    parse: Parse
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/parse.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/parse.js

Statements: 8.43% (7 / 83)      Branches: 0% (0 / 90)      Functions: 0% (0 / 4)      Lines: 8.43% (7 / 83)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169    1   1   1                       1                                                   1                                                               1                                                                                                             1                                                                          
'use strict';
 
var Utils = require('./utils');
 
var has = Object.prototype.hasOwnProperty;
 
var defaults = {
    delimiter: '&',
    depth: 5,
    arrayLimit: 20,
    parameterLimit: 1000,
    strictNullHandling: false,
    plainObjects: false,
    allowPrototypes: false,
    allowDots: false,
    decoder: Utils.decode
};
 
var parseValues = function parseValues(str, options) {
    var obj = {};
    var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
 
    for (var i = 0; i < parts.length; ++i) {
        var part = parts[i];
        var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
 
        var key, val;
        if (pos === -1) {
            key = options.decoder(part);
            val = options.strictNullHandling ? null : '';
        } else {
            key = options.decoder(part.slice(0, pos));
            val = options.decoder(part.slice(pos + 1));
        }
        if (has.call(obj, key)) {
            obj[key] = [].concat(obj[key]).concat(val);
        } else {
            obj[key] = val;
        }
    }
 
    return obj;
};
 
var parseObject = function parseObject(chain, val, options) {
    if (!chain.length) {
        return val;
    }
 
    var root = chain.shift();
 
    var obj;
    if (root === '[]') {
        obj = [];
        obj = obj.concat(parseObject(chain, val, options));
    } else {
        obj = options.plainObjects ? Object.create(null) : {};
        var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
        var index = parseInt(cleanRoot, 10);
        if (
            !isNaN(index) &&
            root !== cleanRoot &&
            String(index) === cleanRoot &&
            index >= 0 &&
            (options.parseArrays && index <= options.arrayLimit)
        ) {
            obj = [];
            obj[index] = parseObject(chain, val, options);
        } else {
            obj[cleanRoot] = parseObject(chain, val, options);
        }
    }
 
    return obj;
};
 
var parseKeys = function parseKeys(givenKey, val, options) {
    if (!givenKey) {
        return;
    }
 
    // Transform dot notation to bracket notation
    var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
 
    // The regex chunks
 
    var brackets = /(\[[^[\]]*])/;
    var child = /(\[[^[\]]*])/g;
 
    // Get the parent
 
    var segment = brackets.exec(key);
    var parent = segment ? key.slice(0, segment.index) : key;
 
    // Stash the parent if it exists
 
    var keys = [];
    if (parent) {
        // If we aren't using plain objects, optionally prefix keys
        // that would overwrite object prototype properties
        if (!options.plainObjects && has.call(Object.prototype, parent)) {
            if (!options.allowPrototypes) {
                return;
            }
        }
 
        keys.push(parent);
    }
 
    // Loop through children appending to the array until we hit depth
 
    var i = 0;
    while ((segment = child.exec(key)) !== null && i < options.depth) {
        i += 1;
        if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) {
            if (!options.allowPrototypes) {
                return;
            }
        }
        keys.push(segment[1]);
    }
 
    // If there's a remainder, just add whatever is left
 
    if (segment) {
        keys.push('[' + key.slice(segment.index) + ']');
    }
 
    return parseObject(keys, val, options);
};
 
module.exports = function (str, opts) {
    var options = opts || {};
 
    if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') {
        throw new TypeError('Decoder has to be a function.');
    }
 
    options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter;
    options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth;
    options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit;
    options.parseArrays = options.parseArrays !== false;
    options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder;
    options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots;
    options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects;
    options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes;
    options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit;
    options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
 
    if (str === '' || str === null || typeof str === 'undefined') {
        return options.plainObjects ? Object.create(null) : {};
    }
 
    var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
    var obj = options.plainObjects ? Object.create(null) : {};
 
    // Iterate over the keys and setup the new object
 
    var keys = Object.keys(tempObj);
    for (var i = 0; i < keys.length; ++i) {
        var key = keys[i];
        var newObj = parseKeys(key, tempObj[key], options);
        obj = Utils.merge(obj, newObj, options);
    }
 
    return Utils.compact(obj);
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/stringify.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/stringify.js

Statements: 6.67% (5 / 75)      Branches: 0% (0 / 77)      Functions: 0% (0 / 5)      Lines: 6.67% (5 / 75)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139    1   1                       1               1                                                                                                       1                                                                                                                            
'use strict';
 
var Utils = require('./utils');
 
var arrayPrefixGenerators = {
    brackets: function brackets(prefix) {
        return prefix + '[]';
    },
    indices: function indices(prefix, key) {
        return prefix + '[' + key + ']';
    },
    repeat: function repeat(prefix) {
        return prefix;
    }
};
 
var defaults = {
    delimiter: '&',
    strictNullHandling: false,
    skipNulls: false,
    encode: true,
    encoder: Utils.encode
};
 
var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) {
    var obj = object;
    if (typeof filter === 'function') {
        obj = filter(prefix, obj);
    } else if (obj instanceof Date) {
        obj = obj.toISOString();
    } else if (obj === null) {
        if (strictNullHandling) {
            return encoder ? encoder(prefix) : prefix;
        }
 
        obj = '';
    }
 
    if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || Utils.isBuffer(obj)) {
        if (encoder) {
            return [encoder(prefix) + '=' + encoder(obj)];
        }
        return [prefix + '=' + String(obj)];
    }
 
    var values = [];
 
    if (typeof obj === 'undefined') {
        return values;
    }
 
    var objKeys;
    if (Array.isArray(filter)) {
        objKeys = filter;
    } else {
        var keys = Object.keys(obj);
        objKeys = sort ? keys.sort(sort) : keys;
    }
 
    for (var i = 0; i < objKeys.length; ++i) {
        var key = objKeys[i];
 
        if (skipNulls && obj[key] === null) {
            continue;
        }
 
        if (Array.isArray(obj)) {
            values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
        } else {
            values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
        }
    }
 
    return values;
};
 
module.exports = function (object, opts) {
    var obj = object;
    var options = opts || {};
    var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter;
    var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
    var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
    var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
    var encoder = encode ? (typeof options.encoder === 'function' ? options.encoder : defaults.encoder) : null;
    var sort = typeof options.sort === 'function' ? options.sort : null;
    var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
    var objKeys;
    var filter;
 
    if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
        throw new TypeError('Encoder has to be a function.');
    }
 
    if (typeof options.filter === 'function') {
        filter = options.filter;
        obj = filter('', obj);
    } else if (Array.isArray(options.filter)) {
        objKeys = filter = options.filter;
    }
 
    var keys = [];
 
    if (typeof obj !== 'object' || obj === null) {
        return '';
    }
 
    var arrayFormat;
    if (options.arrayFormat in arrayPrefixGenerators) {
        arrayFormat = options.arrayFormat;
    } else if ('indices' in options) {
        arrayFormat = options.indices ? 'indices' : 'repeat';
    } else {
        arrayFormat = 'indices';
    }
 
    var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
 
    if (!objKeys) {
        objKeys = Object.keys(obj);
    }
 
    if (sort) {
        objKeys.sort(sort);
    }
 
    for (var i = 0; i < objKeys.length; ++i) {
        var key = objKeys[i];
 
        if (skipNulls && obj[key] === null) {
            continue;
        }
 
        keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
    }
 
    return keys.join(delimiter);
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/utils.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/qs/lib/utils.js

Statements: 14.77% (13 / 88)      Branches: 2.7% (2 / 74)      Functions: 11.11% (1 / 9)      Lines: 14.77% (13 / 88)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170    1 1 1 256     1     1   1                     1                                                                               1               1                                                                                                 1                                                                       1       1                
'use strict';
 
var hexTable = (function () {
    var array = new Array(256);
    for (var i = 0; i < 256; ++i) {
        array[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase();
    }
 
    return array;
}());
 
var has = Object.prototype.hasOwnProperty;
 
exports.arrayToObject = function (source, options) {
    var obj = options.plainObjects ? Object.create(null) : {};
    for (var i = 0; i < source.length; ++i) {
        if (typeof source[i] !== 'undefined') {
            obj[i] = source[i];
        }
    }
 
    return obj;
};
 
exports.merge = function (target, source, options) {
    if (!source) {
        return target;
    }
 
    if (typeof source !== 'object') {
        if (Array.isArray(target)) {
            target.push(source);
        } else if (typeof target === 'object') {
            if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) {
                target[source] = true;
            }
        } else {
            return [target, source];
        }
 
        return target;
    }
 
    if (typeof target !== 'object') {
        return [target].concat(source);
    }
 
    var mergeTarget = target;
    if (Array.isArray(target) && !Array.isArray(source)) {
        mergeTarget = exports.arrayToObject(target, options);
    }
 
    return Object.keys(source).reduce(function (acc, key) {
        var value = source[key];
 
        if (has.call(acc, key)) {
            acc[key] = exports.merge(acc[key], value, options);
        } else {
            acc[key] = value;
        }
        return acc;
    }, mergeTarget);
};
 
exports.decode = function (str) {
    try {
        return decodeURIComponent(str.replace(/\+/g, ' '));
    } catch (e) {
        return str;
    }
};
 
exports.encode = function (str) {
    // This code was originally written by Brian White (mscdex) for the io.js core querystring library.
    // It has been adapted here for stricter adherence to RFC 3986
    if (str.length === 0) {
        return str;
    }
 
    var string = typeof str === 'string' ? str : String(str);
 
    var out = '';
    for (var i = 0; i < string.length; ++i) {
        var c = string.charCodeAt(i);
 
        if (
            c === 0x2D || // -
            c === 0x2E || // .
            c === 0x5F || // _
            c === 0x7E || // ~
            (c >= 0x30 && c <= 0x39) || // 0-9
            (c >= 0x41 && c <= 0x5A) || // a-z
            (c >= 0x61 && c <= 0x7A) // A-Z
        ) {
            out += string.charAt(i);
            continue;
        }
 
        if (c < 0x80) {
            out = out + hexTable[c];
            continue;
        }
 
        if (c < 0x800) {
            out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]);
            continue;
        }
 
        if (c < 0xD800 || c >= 0xE000) {
            out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]);
            continue;
        }
 
        i += 1;
        c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
        out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)];
    }
 
    return out;
};
 
exports.compact = function (obj, references) {
    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }
 
    var refs = references || [];
    var lookup = refs.indexOf(obj);
    if (lookup !== -1) {
        return refs[lookup];
    }
 
    refs.push(obj);
 
    if (Array.isArray(obj)) {
        var compacted = [];
 
        for (var i = 0; i < obj.length; ++i) {
            if (obj[i] && typeof obj[i] === 'object') {
                compacted.push(exports.compact(obj[i], refs));
            } else if (typeof obj[i] !== 'undefined') {
                compacted.push(obj[i]);
            }
        }
 
        return compacted;
    }
 
    var keys = Object.keys(obj);
    for (var j = 0; j < keys.length; ++j) {
        var key = keys[j];
        obj[key] = exports.compact(obj[key], refs);
    }
 
    return obj;
};
 
exports.isRegExp = function (obj) {
    return Object.prototype.toString.call(obj) === '[object RegExp]';
};
 
exports.isBuffer = function (obj) {
    if (obj === null || typeof obj === 'undefined') {
        return false;
    }
 
    return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/

Statements: 100% (1 / 1)      Branches: 100% (0 / 0)      Functions: 100% (0 / 0)      Lines: 100% (1 / 1)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/
File Statements Branches Functions Lines
duplex.js 100% (1 / 1) 100% (0 / 0) 100% (0 / 0) 100% (1 / 1)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/duplex.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/duplex.js

Statements: 100% (1 / 1)      Branches: 100% (0 / 0)      Functions: 100% (0 / 0)      Lines: 100% (1 / 1)      Ignored: none     

1 2 31    
module.exports = require("./lib/_stream_duplex.js")
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/

Statements: 15.93% (134 / 841)      Branches: 2.13% (11 / 516)      Functions: 3.33% (3 / 90)      Lines: 18% (133 / 739)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/
File Statements Branches Functions Lines
_stream_duplex.js 43.59% (17 / 39) 13.64% (3 / 22) 0% (0 / 5) 48.48% (16 / 33)
_stream_readable.js 13.13% (68 / 518) 1.15% (4 / 349) 1.92% (1 / 52) 15.14% (68 / 449)
_stream_writable.js 17.25% (49 / 284) 2.76% (4 / 145) 6.06% (2 / 33) 19.07% (49 / 257)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/_stream_duplex.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/_stream_duplex.js

Statements: 43.59% (17 / 39)      Branches: 13.64% (3 / 22)      Functions: 0% (0 / 5)      Lines: 48.48% (16 / 33)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76                  1               1     1       1 1     1 1   1   1 1 8 8     1                                 1                   1       1          
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.
 
'use strict';
 
/*<replacement>*/
 
var objectKeys = Object.keys || function (obj) {
  var keys = [];
  for (var key in obj) {
    keys.push(key);
  }return keys;
};
/*</replacement>*/
 
module.exports = Duplex;
 
/*<replacement>*/
var processNextTick = require('process-nextick-args');
/*</replacement>*/
 
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
 
var Readable = require('./_stream_readable');
var Writable = require('./_stream_writable');
 
util.inherits(Duplex, Readable);
 
var keys = objectKeys(Writable.prototype);
for (var v = 0; v < keys.length; v++) {
  var method = keys[v];
  if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method];
}
 
function Duplex(options) {
  if (!(this instanceof Duplex)) return new Duplex(options);
 
  Readable.call(this, options);
  Writable.call(this, options);
 
  if (options && options.readable === false) this.readable = false;
 
  if (options && options.writable === false) this.writable = false;
 
  this.allowHalfOpen = true;
  if (options && options.allowHalfOpen === false) this.allowHalfOpen = false;
 
  this.once('end', onend);
}
 
// the no-half-open enforcer
function onend() {
  // if we allow half-open state, or if the writable side ended,
  // then we're ok.
  if (this.allowHalfOpen || this._writableState.ended) return;
 
  // no more data can be written.
  // But allow more writes to happen in this tick.
  processNextTick(onEndNT, this);
}
 
function onEndNT(self) {
  self.end();
}
 
function forEach(xs, f) {
  for (var i = 0, l = xs.length; i < l; i++) {
    f(xs[i], i);
  }
}
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/_stream_readable.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/_stream_readable.js

Statements: 13.13% (68 / 518)      Branches: 1.15% (4 / 349)      Functions: 1.92% (1 / 52)      Lines: 15.14% (68 / 449)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881    1     1       1       1     1   1     1           1 1 1 1   1         1     1 1       1 1 1 1           1   1   1 1                                                                                                                                   1 1                                     1                             1         1       1                                                                                                             1         1               1 1                               1                                                               1                                                                                                                                                                                                       1               1                                   1                   1                       1             1                               1       1                                               1             1                         1                                             1                                 1                     1         1             1                                 1                       1                                                                                                     1                                                 1   1             1                   1             1                       1                   1                         1                                                                                                                         1       1                                                                                             1                         1                 1           1            
'use strict';
 
module.exports = Readable;
 
/*<replacement>*/
var processNextTick = require('process-nextick-args');
/*</replacement>*/
 
/*<replacement>*/
var isArray = require('isarray');
/*</replacement>*/
 
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
 
Readable.ReadableState = ReadableState;
 
var EE = require('events');
 
/*<replacement>*/
var EElistenerCount = function (emitter, type) {
  return emitter.listeners(type).length;
};
/*</replacement>*/
 
/*<replacement>*/
var Stream;
(function () {
  try {
    Stream = require('st' + 'ream');
  } catch (_) {} finally {
    Iif (!Stream) Stream = require('events').EventEmitter;
  }
})();
/*</replacement>*/
 
var Buffer = require('buffer').Buffer;
 
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
 
/*<replacement>*/
var debugUtil = require('util');
var debug = undefined;
Eif (debugUtil && debugUtil.debuglog) {
  debug = debugUtil.debuglog('stream');
} else {
  debug = function () {};
}
/*</replacement>*/
 
var StringDecoder;
 
util.inherits(Readable, Stream);
 
var Duplex;
function ReadableState(options, stream) {
  Duplex = Duplex || require('./_stream_duplex');
 
  options = options || {};
 
  // object stream flag. Used to make read(n) ignore n and to
  // make all the buffer merging and length checks go away
  this.objectMode = !!options.objectMode;
 
  if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode;
 
  // the point at which it stops calling _read() to fill the buffer
  // Note: 0 is a valid value, means "don't call _read preemptively ever"
  var hwm = options.highWaterMark;
  var defaultHwm = this.objectMode ? 16 : 16 * 1024;
  this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
 
  // cast to ints.
  this.highWaterMark = ~ ~this.highWaterMark;
 
  this.buffer = [];
  this.length = 0;
  this.pipes = null;
  this.pipesCount = 0;
  this.flowing = null;
  this.ended = false;
  this.endEmitted = false;
  this.reading = false;
 
  // a flag to be able to tell if the onwrite cb is called immediately,
  // or on a later tick.  We set this to true at first, because any
  // actions that shouldn't happen until "later" should generally also
  // not happen before the first write call.
  this.sync = true;
 
  // whenever we return null, then we set a flag to say
  // that we're awaiting a 'readable' event emission.
  this.needReadable = false;
  this.emittedReadable = false;
  this.readableListening = false;
  this.resumeScheduled = false;
 
  // Crypto is kind of old and crusty.  Historically, its default string
  // encoding is 'binary' so we have to make this configurable.
  // Everything else in the universe uses 'utf8', though.
  this.defaultEncoding = options.defaultEncoding || 'utf8';
 
  // when piping, we only care about 'readable' events that happen
  // after read()ing all the bytes and not getting any pushback.
  this.ranOut = false;
 
  // the number of writers that are awaiting a drain event in .pipe()s
  this.awaitDrain = 0;
 
  // if true, a maybeReadMore has been scheduled
  this.readingMore = false;
 
  this.decoder = null;
  this.encoding = null;
  if (options.encoding) {
    if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
    this.decoder = new StringDecoder(options.encoding);
    this.encoding = options.encoding;
  }
}
 
var Duplex;
function Readable(options) {
  Duplex = Duplex || require('./_stream_duplex');
 
  if (!(this instanceof Readable)) return new Readable(options);
 
  this._readableState = new ReadableState(options, this);
 
  // legacy
  this.readable = true;
 
  if (options && typeof options.read === 'function') this._read = options.read;
 
  Stream.call(this);
}
 
// Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.
Readable.prototype.push = function (chunk, encoding) {
  var state = this._readableState;
 
  if (!state.objectMode && typeof chunk === 'string') {
    encoding = encoding || state.defaultEncoding;
    if (encoding !== state.encoding) {
      chunk = new Buffer(chunk, encoding);
      encoding = '';
    }
  }
 
  return readableAddChunk(this, state, chunk, encoding, false);
};
 
// Unshift should *always* be something directly out of read()
Readable.prototype.unshift = function (chunk) {
  var state = this._readableState;
  return readableAddChunk(this, state, chunk, '', true);
};
 
Readable.prototype.isPaused = function () {
  return this._readableState.flowing === false;
};
 
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
  var er = chunkInvalid(state, chunk);
  if (er) {
    stream.emit('error', er);
  } else if (chunk === null) {
    state.reading = false;
    onEofChunk(stream, state);
  } else if (state.objectMode || chunk && chunk.length > 0) {
    if (state.ended && !addToFront) {
      var e = new Error('stream.push() after EOF');
      stream.emit('error', e);
    } else if (state.endEmitted && addToFront) {
      var e = new Error('stream.unshift() after end event');
      stream.emit('error', e);
    } else {
      var skipAdd;
      if (state.decoder && !addToFront && !encoding) {
        chunk = state.decoder.write(chunk);
        skipAdd = !state.objectMode && chunk.length === 0;
      }
 
      if (!addToFront) state.reading = false;
 
      // Don't add to the buffer if we've decoded to an empty string chunk and
      // we're not in object mode
      if (!skipAdd) {
        // if we want the data now, just emit it.
        if (state.flowing && state.length === 0 && !state.sync) {
          stream.emit('data', chunk);
          stream.read(0);
        } else {
          // update the buffer info.
          state.length += state.objectMode ? 1 : chunk.length;
          if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);
 
          if (state.needReadable) emitReadable(stream);
        }
      }
 
      maybeReadMore(stream, state);
    }
  } else if (!addToFront) {
    state.reading = false;
  }
 
  return needMoreData(state);
}
 
// if it's past the high water mark, we can push in some more.
// Also, if we have no data yet, we can stand some
// more bytes.  This is to work around cases where hwm=0,
// such as the repl.  Also, if the push() triggered a
// readable event, and the user called read(largeNumber) such that
// needReadable was set, then we ought to push more, so that another
// 'readable' event will be triggered.
function needMoreData(state) {
  return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0);
}
 
// backwards compatibility.
Readable.prototype.setEncoding = function (enc) {
  if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
  this._readableState.decoder = new StringDecoder(enc);
  this._readableState.encoding = enc;
  return this;
};
 
// Don't raise the hwm > 8MB
var MAX_HWM = 0x800000;
function computeNewHighWaterMark(n) {
  if (n >= MAX_HWM) {
    n = MAX_HWM;
  } else {
    // Get the next highest power of 2
    n--;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    n++;
  }
  return n;
}
 
function howMuchToRead(n, state) {
  if (state.length === 0 && state.ended) return 0;
 
  if (state.objectMode) return n === 0 ? 0 : 1;
 
  if (n === null || isNaN(n)) {
    // only flow one buffer at a time
    if (state.flowing && state.buffer.length) return state.buffer[0].length;else return state.length;
  }
 
  if (n <= 0) return 0;
 
  // If we're asking for more than the target buffer level,
  // then raise the water mark.  Bump up to the next highest
  // power of 2, to prevent increasing it excessively in tiny
  // amounts.
  if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);
 
  // don't have that much.  return null, unless we've ended.
  if (n > state.length) {
    if (!state.ended) {
      state.needReadable = true;
      return 0;
    } else {
      return state.length;
    }
  }
 
  return n;
}
 
// you can override either this method, or the async _read(n) below.
Readable.prototype.read = function (n) {
  debug('read', n);
  var state = this._readableState;
  var nOrig = n;
 
  if (typeof n !== 'number' || n > 0) state.emittedReadable = false;
 
  // if we're doing read(0) to trigger a readable event, but we
  // already have a bunch of data in the buffer, then just trigger
  // the 'readable' event and move on.
  if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) {
    debug('read: emitReadable', state.length, state.ended);
    if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);
    return null;
  }
 
  n = howMuchToRead(n, state);
 
  // if we've ended, and we're now clear, then finish it up.
  if (n === 0 && state.ended) {
    if (state.length === 0) endReadable(this);
    return null;
  }
 
  // All the actual chunk generation logic needs to be
  // *below* the call to _read.  The reason is that in certain
  // synthetic stream cases, such as passthrough streams, _read
  // may be a completely synchronous operation which may change
  // the state of the read buffer, providing enough data when
  // before there was *not* enough.
  //
  // So, the steps are:
  // 1. Figure out what the state of things will be after we do
  // a read from the buffer.
  //
  // 2. If that resulting state will trigger a _read, then call _read.
  // Note that this may be asynchronous, or synchronous.  Yes, it is
  // deeply ugly to write APIs this way, but that still doesn't mean
  // that the Readable class should behave improperly, as streams are
  // designed to be sync/async agnostic.
  // Take note if the _read call is sync or async (ie, if the read call
  // has returned yet), so that we know whether or not it's safe to emit
  // 'readable' etc.
  //
  // 3. Actually pull the requested chunks out of the buffer and return.
 
  // if we need a readable event, then we need to do some reading.
  var doRead = state.needReadable;
  debug('need readable', doRead);
 
  // if we currently have less than the highWaterMark, then also read some
  if (state.length === 0 || state.length - n < state.highWaterMark) {
    doRead = true;
    debug('length less than watermark', doRead);
  }
 
  // however, if we've ended, then there's no point, and if we're already
  // reading, then it's unnecessary.
  if (state.ended || state.reading) {
    doRead = false;
    debug('reading or ended', doRead);
  }
 
  if (doRead) {
    debug('do read');
    state.reading = true;
    state.sync = true;
    // if the length is currently zero, then we *need* a readable event.
    if (state.length === 0) state.needReadable = true;
    // call internal read method
    this._read(state.highWaterMark);
    state.sync = false;
  }
 
  // If _read pushed data synchronously, then `reading` will be false,
  // and we need to re-evaluate how much data we can return to the user.
  if (doRead && !state.reading) n = howMuchToRead(nOrig, state);
 
  var ret;
  if (n > 0) ret = fromList(n, state);else ret = null;
 
  if (ret === null) {
    state.needReadable = true;
    n = 0;
  }
 
  state.length -= n;
 
  // If we have nothing in the buffer, then we want to know
  // as soon as we *do* get something into the buffer.
  if (state.length === 0 && !state.ended) state.needReadable = true;
 
  // If we tried to read() past the EOF, then emit end on the next tick.
  if (nOrig !== n && state.ended && state.length === 0) endReadable(this);
 
  if (ret !== null) this.emit('data', ret);
 
  return ret;
};
 
function chunkInvalid(state, chunk) {
  var er = null;
  if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) {
    er = new TypeError('Invalid non-string/buffer chunk');
  }
  return er;
}
 
function onEofChunk(stream, state) {
  if (state.ended) return;
  if (state.decoder) {
    var chunk = state.decoder.end();
    if (chunk && chunk.length) {
      state.buffer.push(chunk);
      state.length += state.objectMode ? 1 : chunk.length;
    }
  }
  state.ended = true;
 
  // emit 'readable' now to make sure it gets picked up.
  emitReadable(stream);
}
 
// Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow.  This way, it might trigger
// a nextTick recursion warning, but that's not so bad.
function emitReadable(stream) {
  var state = stream._readableState;
  state.needReadable = false;
  if (!state.emittedReadable) {
    debug('emitReadable', state.flowing);
    state.emittedReadable = true;
    if (state.sync) processNextTick(emitReadable_, stream);else emitReadable_(stream);
  }
}
 
function emitReadable_(stream) {
  debug('emit readable');
  stream.emit('readable');
  flow(stream);
}
 
// at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data.  that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.
function maybeReadMore(stream, state) {
  if (!state.readingMore) {
    state.readingMore = true;
    processNextTick(maybeReadMore_, stream, state);
  }
}
 
function maybeReadMore_(stream, state) {
  var len = state.length;
  while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) {
    debug('maybeReadMore read 0');
    stream.read(0);
    if (len === state.length)
      // didn't get any data, stop spinning.
      break;else len = state.length;
  }
  state.readingMore = false;
}
 
// abstract method.  to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.
Readable.prototype._read = function (n) {
  this.emit('error', new Error('not implemented'));
};
 
Readable.prototype.pipe = function (dest, pipeOpts) {
  var src = this;
  var state = this._readableState;
 
  switch (state.pipesCount) {
    case 0:
      state.pipes = dest;
      break;
    case 1:
      state.pipes = [state.pipes, dest];
      break;
    default:
      state.pipes.push(dest);
      break;
  }
  state.pipesCount += 1;
  debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
 
  var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;
 
  var endFn = doEnd ? onend : cleanup;
  if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn);
 
  dest.on('unpipe', onunpipe);
  function onunpipe(readable) {
    debug('onunpipe');
    if (readable === src) {
      cleanup();
    }
  }
 
  function onend() {
    debug('onend');
    dest.end();
  }
 
  // when the dest drains, it reduces the awaitDrain counter
  // on the source.  This would be more elegant with a .once()
  // handler in flow(), but adding and removing repeatedly is
  // too slow.
  var ondrain = pipeOnDrain(src);
  dest.on('drain', ondrain);
 
  var cleanedUp = false;
  function cleanup() {
    debug('cleanup');
    // cleanup event handlers once the pipe is broken
    dest.removeListener('close', onclose);
    dest.removeListener('finish', onfinish);
    dest.removeListener('drain', ondrain);
    dest.removeListener('error', onerror);
    dest.removeListener('unpipe', onunpipe);
    src.removeListener('end', onend);
    src.removeListener('end', cleanup);
    src.removeListener('data', ondata);
 
    cleanedUp = true;
 
    // if the reader is waiting for a drain event from this
    // specific writer, then it would cause it to never start
    // flowing again.
    // So, if this is awaiting a drain, then we just call it now.
    // If we don't know, then assume that we are waiting for one.
    if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();
  }
 
  src.on('data', ondata);
  function ondata(chunk) {
    debug('ondata');
    var ret = dest.write(chunk);
    if (false === ret) {
      // If the user unpiped during `dest.write()`, it is possible
      // to get stuck in a permanently paused state if that write
      // also returned false.
      if (state.pipesCount === 1 && state.pipes[0] === dest && src.listenerCount('data') === 1 && !cleanedUp) {
        debug('false write response, pause', src._readableState.awaitDrain);
        src._readableState.awaitDrain++;
      }
      src.pause();
    }
  }
 
  // if the dest has an error, then stop piping into it.
  // however, don't suppress the throwing behavior for this.
  function onerror(er) {
    debug('onerror', er);
    unpipe();
    dest.removeListener('error', onerror);
    if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er);
  }
  // This is a brutally ugly hack to make sure that our error handler
  // is attached before any userland ones.  NEVER DO THIS.
  if (!dest._events || !dest._events.error) dest.on('error', onerror);else if (isArray(dest._events.error)) dest._events.error.unshift(onerror);else dest._events.error = [onerror, dest._events.error];
 
  // Both close and finish should trigger unpipe, but only once.
  function onclose() {
    dest.removeListener('finish', onfinish);
    unpipe();
  }
  dest.once('close', onclose);
  function onfinish() {
    debug('onfinish');
    dest.removeListener('close', onclose);
    unpipe();
  }
  dest.once('finish', onfinish);
 
  function unpipe() {
    debug('unpipe');
    src.unpipe(dest);
  }
 
  // tell the dest that it's being piped to
  dest.emit('pipe', src);
 
  // start the flow if it hasn't been started already.
  if (!state.flowing) {
    debug('pipe resume');
    src.resume();
  }
 
  return dest;
};
 
function pipeOnDrain(src) {
  return function () {
    var state = src._readableState;
    debug('pipeOnDrain', state.awaitDrain);
    if (state.awaitDrain) state.awaitDrain--;
    if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {
      state.flowing = true;
      flow(src);
    }
  };
}
 
Readable.prototype.unpipe = function (dest) {
  var state = this._readableState;
 
  // if we're not piping anywhere, then do nothing.
  if (state.pipesCount === 0) return this;
 
  // just one destination.  most common case.
  if (state.pipesCount === 1) {
    // passed in one, but it's not the right one.
    if (dest && dest !== state.pipes) return this;
 
    if (!dest) dest = state.pipes;
 
    // got a match.
    state.pipes = null;
    state.pipesCount = 0;
    state.flowing = false;
    if (dest) dest.emit('unpipe', this);
    return this;
  }
 
  // slow case. multiple pipe destinations.
 
  if (!dest) {
    // remove all.
    var dests = state.pipes;
    var len = state.pipesCount;
    state.pipes = null;
    state.pipesCount = 0;
    state.flowing = false;
 
    for (var _i = 0; _i < len; _i++) {
      dests[_i].emit('unpipe', this);
    }return this;
  }
 
  // try to find the right one.
  var i = indexOf(state.pipes, dest);
  if (i === -1) return this;
 
  state.pipes.splice(i, 1);
  state.pipesCount -= 1;
  if (state.pipesCount === 1) state.pipes = state.pipes[0];
 
  dest.emit('unpipe', this);
 
  return this;
};
 
// set up data events if they are asked for
// Ensure readable listeners eventually get something
Readable.prototype.on = function (ev, fn) {
  var res = Stream.prototype.on.call(this, ev, fn);
 
  // If listening to data, and it has not explicitly been paused,
  // then call resume to start the flow of data on the next tick.
  if (ev === 'data' && false !== this._readableState.flowing) {
    this.resume();
  }
 
  if (ev === 'readable' && !this._readableState.endEmitted) {
    var state = this._readableState;
    if (!state.readableListening) {
      state.readableListening = true;
      state.emittedReadable = false;
      state.needReadable = true;
      if (!state.reading) {
        processNextTick(nReadingNextTick, this);
      } else if (state.length) {
        emitReadable(this, state);
      }
    }
  }
 
  return res;
};
Readable.prototype.addListener = Readable.prototype.on;
 
function nReadingNextTick(self) {
  debug('readable nexttick read 0');
  self.read(0);
}
 
// pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.
Readable.prototype.resume = function () {
  var state = this._readableState;
  if (!state.flowing) {
    debug('resume');
    state.flowing = true;
    resume(this, state);
  }
  return this;
};
 
function resume(stream, state) {
  if (!state.resumeScheduled) {
    state.resumeScheduled = true;
    processNextTick(resume_, stream, state);
  }
}
 
function resume_(stream, state) {
  if (!state.reading) {
    debug('resume read 0');
    stream.read(0);
  }
 
  state.resumeScheduled = false;
  stream.emit('resume');
  flow(stream);
  if (state.flowing && !state.reading) stream.read(0);
}
 
Readable.prototype.pause = function () {
  debug('call pause flowing=%j', this._readableState.flowing);
  if (false !== this._readableState.flowing) {
    debug('pause');
    this._readableState.flowing = false;
    this.emit('pause');
  }
  return this;
};
 
function flow(stream) {
  var state = stream._readableState;
  debug('flow', state.flowing);
  if (state.flowing) {
    do {
      var chunk = stream.read();
    } while (null !== chunk && state.flowing);
  }
}
 
// wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.
Readable.prototype.wrap = function (stream) {
  var state = this._readableState;
  var paused = false;
 
  var self = this;
  stream.on('end', function () {
    debug('wrapped end');
    if (state.decoder && !state.ended) {
      var chunk = state.decoder.end();
      if (chunk && chunk.length) self.push(chunk);
    }
 
    self.push(null);
  });
 
  stream.on('data', function (chunk) {
    debug('wrapped data');
    if (state.decoder) chunk = state.decoder.write(chunk);
 
    // don't skip over falsy values in objectMode
    if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;
 
    var ret = self.push(chunk);
    if (!ret) {
      paused = true;
      stream.pause();
    }
  });
 
  // proxy all the other methods.
  // important when wrapping filters and duplexes.
  for (var i in stream) {
    if (this[i] === undefined && typeof stream[i] === 'function') {
      this[i] = function (method) {
        return function () {
          return stream[method].apply(stream, arguments);
        };
      }(i);
    }
  }
 
  // proxy certain important events.
  var events = ['error', 'close', 'destroy', 'pause', 'resume'];
  forEach(events, function (ev) {
    stream.on(ev, self.emit.bind(self, ev));
  });
 
  // when we try to consume some more bytes, simply unpause the
  // underlying stream.
  self._read = function (n) {
    debug('wrapped _read', n);
    if (paused) {
      paused = false;
      stream.resume();
    }
  };
 
  return self;
};
 
// exposed for testing purposes only.
Readable._fromList = fromList;
 
// Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
function fromList(n, state) {
  var list = state.buffer;
  var length = state.length;
  var stringMode = !!state.decoder;
  var objectMode = !!state.objectMode;
  var ret;
 
  // nothing in the list, definitely empty.
  if (list.length === 0) return null;
 
  if (length === 0) ret = null;else if (objectMode) ret = list.shift();else if (!n || n >= length) {
    // read it all, truncate the array.
    if (stringMode) ret = list.join('');else if (list.length === 1) ret = list[0];else ret = Buffer.concat(list, length);
    list.length = 0;
  } else {
    // read just some of it.
    if (n < list[0].length) {
      // just take a part of the first list item.
      // slice is the same for buffers and strings.
      var buf = list[0];
      ret = buf.slice(0, n);
      list[0] = buf.slice(n);
    } else if (n === list[0].length) {
      // first list is a perfect match
      ret = list.shift();
    } else {
      // complex case.
      // we have enough to cover it, but it spans past the first buffer.
      if (stringMode) ret = '';else ret = new Buffer(n);
 
      var c = 0;
      for (var i = 0, l = list.length; i < l && c < n; i++) {
        var buf = list[0];
        var cpy = Math.min(n - c, buf.length);
 
        if (stringMode) ret += buf.slice(0, cpy);else buf.copy(ret, c, 0, cpy);
 
        if (cpy < buf.length) list[0] = buf.slice(cpy);else list.shift();
 
        c += cpy;
      }
    }
  }
 
  return ret;
}
 
function endReadable(stream) {
  var state = stream._readableState;
 
  // If we get here before consuming all the bytes, then that is a
  // bug in node.  Should never happen.
  if (state.length > 0) throw new Error('endReadable called on non-empty stream');
 
  if (!state.endEmitted) {
    state.ended = true;
    processNextTick(endReadableNT, state, stream);
  }
}
 
function endReadableNT(state, stream) {
  // Check that we didn't get one last unshift.
  if (!state.endEmitted && state.length === 0) {
    state.endEmitted = true;
    stream.readable = false;
    stream.emit('end');
  }
}
 
function forEach(xs, f) {
  for (var i = 0, l = xs.length; i < l; i++) {
    f(xs[i], i);
  }
}
 
function indexOf(xs, x) {
  for (var i = 0, l = xs.length; i < l; i++) {
    if (xs[i] === x) return i;
  }
  return -1;
}
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/_stream_writable.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/readable-stream/lib/_stream_writable.js

Statements: 17.25% (49 / 284)      Branches: 2.76% (4 / 145)      Functions: 6.06% (2 / 33)      Lines: 19.07% (49 / 257)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517            1     1       1       1     1     1 1       1           1 1 1 1   1         1   1   1   1             1 1                                                                                                                                                                                               1                   1 1 1               1 1                                           1       1                       1                       1                                         1           1                   1             1                   1                                                       1                 1               1             1                                                 1                   1               1                                                                                                         1       1   1                                               1       1             1                           1                       1                                            
// A bit simpler than readable streams.
// Implement an async ._write(chunk, encoding, cb), and it'll handle all
// the drain event emission and buffering.
 
'use strict';
 
module.exports = Writable;
 
/*<replacement>*/
var processNextTick = require('process-nextick-args');
/*</replacement>*/
 
/*<replacement>*/
var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick;
/*</replacement>*/
 
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
 
Writable.WritableState = WritableState;
 
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
 
/*<replacement>*/
var internalUtil = {
  deprecate: require('util-deprecate')
};
/*</replacement>*/
 
/*<replacement>*/
var Stream;
(function () {
  try {
    Stream = require('st' + 'ream');
  } catch (_) {} finally {
    Iif (!Stream) Stream = require('events').EventEmitter;
  }
})();
/*</replacement>*/
 
var Buffer = require('buffer').Buffer;
 
util.inherits(Writable, Stream);
 
function nop() {}
 
function WriteReq(chunk, encoding, cb) {
  this.chunk = chunk;
  this.encoding = encoding;
  this.callback = cb;
  this.next = null;
}
 
var Duplex;
function WritableState(options, stream) {
  Duplex = Duplex || require('./_stream_duplex');
 
  options = options || {};
 
  // object stream flag to indicate whether or not this stream
  // contains buffers or objects.
  this.objectMode = !!options.objectMode;
 
  if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode;
 
  // the point at which write() starts returning false
  // Note: 0 is a valid value, means that we always return false if
  // the entire buffer is not flushed immediately on write()
  var hwm = options.highWaterMark;
  var defaultHwm = this.objectMode ? 16 : 16 * 1024;
  this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
 
  // cast to ints.
  this.highWaterMark = ~ ~this.highWaterMark;
 
  this.needDrain = false;
  // at the start of calling end()
  this.ending = false;
  // when end() has been called, and returned
  this.ended = false;
  // when 'finish' is emitted
  this.finished = false;
 
  // should we decode strings into buffers before passing to _write?
  // this is here so that some node-core streams can optimize string
  // handling at a lower level.
  var noDecode = options.decodeStrings === false;
  this.decodeStrings = !noDecode;
 
  // Crypto is kind of old and crusty.  Historically, its default string
  // encoding is 'binary' so we have to make this configurable.
  // Everything else in the universe uses 'utf8', though.
  this.defaultEncoding = options.defaultEncoding || 'utf8';
 
  // not an actual buffer we keep track of, but a measurement
  // of how much we're waiting to get pushed to some underlying
  // socket or file.
  this.length = 0;
 
  // a flag to see when we're in the middle of a write.
  this.writing = false;
 
  // when true all writes will be buffered until .uncork() call
  this.corked = 0;
 
  // a flag to be able to tell if the onwrite cb is called immediately,
  // or on a later tick.  We set this to true at first, because any
  // actions that shouldn't happen until "later" should generally also
  // not happen before the first write call.
  this.sync = true;
 
  // a flag to know if we're processing previously buffered items, which
  // may call the _write() callback in the same tick, so that we don't
  // end up in an overlapped onwrite situation.
  this.bufferProcessing = false;
 
  // the callback that's passed to _write(chunk,cb)
  this.onwrite = function (er) {
    onwrite(stream, er);
  };
 
  // the callback that the user supplies to write(chunk,encoding,cb)
  this.writecb = null;
 
  // the amount that is being written when _write is called.
  this.writelen = 0;
 
  this.bufferedRequest = null;
  this.lastBufferedRequest = null;
 
  // number of pending user-supplied write callbacks
  // this must be 0 before 'finish' can be emitted
  this.pendingcb = 0;
 
  // emit prefinish if the only thing we're waiting for is _write cbs
  // This is relevant for synchronous Transform streams
  this.prefinished = false;
 
  // True if the error was already emitted and should not be thrown again
  this.errorEmitted = false;
 
  // count buffered requests
  this.bufferedRequestCount = 0;
 
  // create the two objects needed to store the corked requests
  // they are not a linked list, as no new elements are inserted in there
  this.corkedRequestsFree = new CorkedRequest(this);
  this.corkedRequestsFree.next = new CorkedRequest(this);
}
 
WritableState.prototype.getBuffer = function writableStateGetBuffer() {
  var current = this.bufferedRequest;
  var out = [];
  while (current) {
    out.push(current);
    current = current.next;
  }
  return out;
};
 
(function () {
  try {
    Object.defineProperty(WritableState.prototype, 'buffer', {
      get: internalUtil.deprecate(function () {
        return this.getBuffer();
      }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.')
    });
  } catch (_) {}
})();
 
var Duplex;
function Writable(options) {
  Duplex = Duplex || require('./_stream_duplex');
 
  // Writable ctor is applied to Duplexes, though they're not
  // instanceof Writable, they're instanceof Readable.
  if (!(this instanceof Writable) && !(this instanceof Duplex)) return new Writable(options);
 
  this._writableState = new WritableState(options, this);
 
  // legacy.
  this.writable = true;
 
  if (options) {
    if (typeof options.write === 'function') this._write = options.write;
 
    if (typeof options.writev === 'function') this._writev = options.writev;
  }
 
  Stream.call(this);
}
 
// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function () {
  this.emit('error', new Error('Cannot pipe. Not readable.'));
};
 
function writeAfterEnd(stream, cb) {
  var er = new Error('write after end');
  // TODO: defer error events consistently everywhere, not just the cb
  stream.emit('error', er);
  processNextTick(cb, er);
}
 
// If we get something that is not a buffer, string, null, or undefined,
// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
function validChunk(stream, state, chunk, cb) {
  var valid = true;
 
  if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) {
    var er = new TypeError('Invalid non-string/buffer chunk');
    stream.emit('error', er);
    processNextTick(cb, er);
    valid = false;
  }
  return valid;
}
 
Writable.prototype.write = function (chunk, encoding, cb) {
  var state = this._writableState;
  var ret = false;
 
  if (typeof encoding === 'function') {
    cb = encoding;
    encoding = null;
  }
 
  if (Buffer.isBuffer(chunk)) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;
 
  if (typeof cb !== 'function') cb = nop;
 
  if (state.ended) writeAfterEnd(this, cb);else if (validChunk(this, state, chunk, cb)) {
    state.pendingcb++;
    ret = writeOrBuffer(this, state, chunk, encoding, cb);
  }
 
  return ret;
};
 
Writable.prototype.cork = function () {
  var state = this._writableState;
 
  state.corked++;
};
 
Writable.prototype.uncork = function () {
  var state = this._writableState;
 
  if (state.corked) {
    state.corked--;
 
    if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
  }
};
 
Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
  // node::ParseEncoding() requires lower case.
  if (typeof encoding === 'string') encoding = encoding.toLowerCase();
  if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding);
  this._writableState.defaultEncoding = encoding;
};
 
function decodeChunk(state, chunk, encoding) {
  if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {
    chunk = new Buffer(chunk, encoding);
  }
  return chunk;
}
 
// if we're already writing something, then just put this
// in the queue, and wait our turn.  Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, cb) {
  chunk = decodeChunk(state, chunk, encoding);
 
  if (Buffer.isBuffer(chunk)) encoding = 'buffer';
  var len = state.objectMode ? 1 : chunk.length;
 
  state.length += len;
 
  var ret = state.length < state.highWaterMark;
  // we must ensure that previous needDrain will not be reset to false.
  if (!ret) state.needDrain = true;
 
  if (state.writing || state.corked) {
    var last = state.lastBufferedRequest;
    state.lastBufferedRequest = new WriteReq(chunk, encoding, cb);
    if (last) {
      last.next = state.lastBufferedRequest;
    } else {
      state.bufferedRequest = state.lastBufferedRequest;
    }
    state.bufferedRequestCount += 1;
  } else {
    doWrite(stream, state, false, len, chunk, encoding, cb);
  }
 
  return ret;
}
 
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
  state.writelen = len;
  state.writecb = cb;
  state.writing = true;
  state.sync = true;
  if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);
  state.sync = false;
}
 
function onwriteError(stream, state, sync, er, cb) {
  --state.pendingcb;
  if (sync) processNextTick(cb, er);else cb(er);
 
  stream._writableState.errorEmitted = true;
  stream.emit('error', er);
}
 
function onwriteStateUpdate(state) {
  state.writing = false;
  state.writecb = null;
  state.length -= state.writelen;
  state.writelen = 0;
}
 
function onwrite(stream, er) {
  var state = stream._writableState;
  var sync = state.sync;
  var cb = state.writecb;
 
  onwriteStateUpdate(state);
 
  if (er) onwriteError(stream, state, sync, er, cb);else {
    // Check if we're actually ready to finish, but don't emit yet
    var finished = needFinish(state);
 
    if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {
      clearBuffer(stream, state);
    }
 
    if (sync) {
      /*<replacement>*/
      asyncWrite(afterWrite, stream, state, finished, cb);
      /*</replacement>*/
    } else {
        afterWrite(stream, state, finished, cb);
      }
  }
}
 
function afterWrite(stream, state, finished, cb) {
  if (!finished) onwriteDrain(stream, state);
  state.pendingcb--;
  cb();
  finishMaybe(stream, state);
}
 
// Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.
function onwriteDrain(stream, state) {
  if (state.length === 0 && state.needDrain) {
    state.needDrain = false;
    stream.emit('drain');
  }
}
 
// if there's something in the buffer waiting, then process it
function clearBuffer(stream, state) {
  state.bufferProcessing = true;
  var entry = state.bufferedRequest;
 
  if (stream._writev && entry && entry.next) {
    // Fast case, write everything using _writev()
    var l = state.bufferedRequestCount;
    var buffer = new Array(l);
    var holder = state.corkedRequestsFree;
    holder.entry = entry;
 
    var count = 0;
    while (entry) {
      buffer[count] = entry;
      entry = entry.next;
      count += 1;
    }
 
    doWrite(stream, state, true, state.length, buffer, '', holder.finish);
 
    // doWrite is always async, defer these to save a bit of time
    // as the hot path ends with doWrite
    state.pendingcb++;
    state.lastBufferedRequest = null;
    state.corkedRequestsFree = holder.next;
    holder.next = null;
  } else {
    // Slow case, write chunks one-by-one
    while (entry) {
      var chunk = entry.chunk;
      var encoding = entry.encoding;
      var cb = entry.callback;
      var len = state.objectMode ? 1 : chunk.length;
 
      doWrite(stream, state, false, len, chunk, encoding, cb);
      entry = entry.next;
      // if we didn't call the onwrite immediately, then
      // it means that we need to wait until it does.
      // also, that means that the chunk and cb are currently
      // being processed, so move the buffer counter past them.
      if (state.writing) {
        break;
      }
    }
 
    if (entry === null) state.lastBufferedRequest = null;
  }
 
  state.bufferedRequestCount = 0;
  state.bufferedRequest = entry;
  state.bufferProcessing = false;
}
 
Writable.prototype._write = function (chunk, encoding, cb) {
  cb(new Error('not implemented'));
};
 
Writable.prototype._writev = null;
 
Writable.prototype.end = function (chunk, encoding, cb) {
  var state = this._writableState;
 
  if (typeof chunk === 'function') {
    cb = chunk;
    chunk = null;
    encoding = null;
  } else if (typeof encoding === 'function') {
    cb = encoding;
    encoding = null;
  }
 
  if (chunk !== null && chunk !== undefined) this.write(chunk, encoding);
 
  // .end() fully uncorks
  if (state.corked) {
    state.corked = 1;
    this.uncork();
  }
 
  // ignore unnecessary end() calls.
  if (!state.ending && !state.finished) endWritable(this, state, cb);
};
 
function needFinish(state) {
  return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;
}
 
function prefinish(stream, state) {
  if (!state.prefinished) {
    state.prefinished = true;
    stream.emit('prefinish');
  }
}
 
function finishMaybe(stream, state) {
  var need = needFinish(state);
  if (need) {
    if (state.pendingcb === 0) {
      prefinish(stream, state);
      state.finished = true;
      stream.emit('finish');
    } else {
      prefinish(stream, state);
    }
  }
  return need;
}
 
function endWritable(stream, state, cb) {
  state.ending = true;
  finishMaybe(stream, state);
  if (cb) {
    if (state.finished) processNextTick(cb);else stream.once('finish', cb);
  }
  state.ended = true;
  stream.writable = false;
}
 
// It seems a linked list but it is not
// there will be only 2 of these for each stream
function CorkedRequest(state) {
  var _this = this;
 
  this.next = null;
  this.entry = null;
 
  this.finish = function (err) {
    var entry = _this.entry;
    _this.entry = null;
    while (entry) {
      var cb = entry.callback;
      state.pendingcb--;
      cb(err);
      entry = entry.next;
    }
    if (state.corkedRequestsFree) {
      state.corkedRequestsFree.next = _this;
    } else {
      state.corkedRequestsFree = _this;
    }
  };
}
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/

Statements: 7.88% (66 / 838)      Branches: 0.16% (1 / 641)      Functions: 1.3% (1 / 77)      Lines: 7.96% (66 / 829)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/
File Statements Branches Functions Lines
index.js 30.26% (23 / 76) 0% (0 / 28) 7.69% (1 / 13) 30.26% (23 / 76)
request.js 5.64% (43 / 762) 0.16% (1 / 613) 0% (0 / 64) 5.71% (43 / 753)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/index.js

Statements: 30.26% (23 / 76)      Branches: 0% (0 / 28)      Functions: 7.69% (1 / 13)      Lines: 30.26% (23 / 76)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159                                1       1         1                                   1                           1 7 7               1 1 1 1 1 1 1   1       1       1                                           1                                             1                             1 1 1     1                    
// Copyright 2010-2012 Mikeal Rogers
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
 
'use strict'
 
var extend                = require('extend')
  , cookies               = require('./lib/cookies')
  , helpers               = require('./lib/helpers')
 
var isFunction            = helpers.isFunction
  , paramsHaveRequestBody = helpers.paramsHaveRequestBody
 
 
// organize params for patch, post, put, head, del
function initParams(uri, options, callback) {
  if (typeof options === 'function') {
    callback = options
  }
 
  var params = {}
  if (typeof options === 'object') {
    extend(params, options, {uri: uri})
  } else if (typeof uri === 'string') {
    extend(params, {uri: uri})
  } else {
    extend(params, uri)
  }
 
  params.callback = callback || params.callback
  return params
}
 
function request (uri, options, callback) {
  if (typeof uri === 'undefined') {
    throw new Error('undefined is not a valid uri or options object.')
  }
 
  var params = initParams(uri, options, callback)
 
  if (params.method === 'HEAD' && paramsHaveRequestBody(params)) {
    throw new Error('HTTP HEAD requests MUST NOT include a request body.')
  }
 
  return new request.Request(params)
}
 
function verbFunc (verb) {
  var method = verb.toUpperCase()
  return function (uri, options, callback) {
    var params = initParams(uri, options, callback)
    params.method = method
    return request(params, params.callback)
  }
}
 
// define like this to please codeintel/intellisense IDEs
request.get = verbFunc('get')
request.head = verbFunc('head')
request.post = verbFunc('post')
request.put = verbFunc('put')
request.patch = verbFunc('patch')
request.del = verbFunc('delete')
request['delete'] = verbFunc('delete')
 
request.jar = function (store) {
  return cookies.jar(store)
}
 
request.cookie = function (str) {
  return cookies.parse(str)
}
 
function wrapRequestMethod (method, options, requester, verb) {
 
  return function (uri, opts, callback) {
    var params = initParams(uri, opts, callback)
 
    var target = {}
    extend(true, target, options, params)
 
    target.pool = params.pool || options.pool
 
    if (verb) {
      target.method = verb.toUpperCase()
    }
 
    if (isFunction(requester)) {
      method = requester
    }
 
    return method(target, target.callback)
  }
}
 
request.defaults = function (options, requester) {
  var self = this
 
  options = options || {}
 
  if (typeof options === 'function') {
    requester = options
    options = {}
  }
 
  var defaults      = wrapRequestMethod(self, options, requester)
 
  var verbs = ['get', 'head', 'post', 'put', 'patch', 'del', 'delete']
  verbs.forEach(function(verb) {
    defaults[verb]  = wrapRequestMethod(self[verb], options, requester, verb)
  })
 
  defaults.cookie   = wrapRequestMethod(self.cookie, options, requester)
  defaults.jar      = self.jar
  defaults.defaults = self.defaults
  return defaults
}
 
request.forever = function (agentOptions, optionsArg) {
  var options = {}
  if (optionsArg) {
    extend(options, optionsArg)
  }
  if (agentOptions) {
    options.agentOptions = agentOptions
  }
 
  options.forever = true
  return request.defaults(options)
}
 
// Exports
 
module.exports = request
request.Request = require('./request')
request.initParams = initParams
 
// Backwards compatibility for request.debug
Object.defineProperty(request, 'debug', {
  enumerable : true,
  get : function() {
    return request.Request.debug
  },
  set : function(debug) {
    request.Request.debug = debug
  }
})
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/request.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/request.js

Statements: 5.64% (43 / 762)      Branches: 0.16% (1 / 613)      Functions: 0% (0 / 64)      Lines: 5.71% (43 / 753)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436    1                                                           1                 1   1                           1                                 1                   1                   1                                                                         1     1 1         1   1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       1                                                                                                                                                                                                                                                                                                                                                   1                                                                                                                                                                                                                                                     1                                                                                                                                                                         1                                     1                                                                                                                                                                                                                                                                                                                                                         1                                                                                                                     1                           1                                                                             1                                                         1                                       1                     1                                                                       1                                   1                             1             1                                                                                                             1                               1       1               1                                                                       1                                     1                     1                           1               1               1                 1     1         1 1    
'use strict'
 
var http = require('http')
  , https = require('https')
  , url = require('url')
  , util = require('util')
  , stream = require('stream')
  , zlib = require('zlib')
  , bl = require('bl')
  , hawk = require('hawk')
  , aws2 = require('aws-sign2')
  , aws4 = require('aws4')
  , httpSignature = require('http-signature')
  , mime = require('mime-types')
  , stringstream = require('stringstream')
  , caseless = require('caseless')
  , ForeverAgent = require('forever-agent')
  , FormData = require('form-data')
  , extend = require('extend')
  , isstream = require('isstream')
  , isTypedArray = require('is-typedarray').strict
  , helpers = require('./lib/helpers')
  , cookies = require('./lib/cookies')
  , getProxyFromURI = require('./lib/getProxyFromURI')
  , Querystring = require('./lib/querystring').Querystring
  , Har = require('./lib/har').Har
  , Auth = require('./lib/auth').Auth
  , OAuth = require('./lib/oauth').OAuth
  , Multipart = require('./lib/multipart').Multipart
  , Redirect = require('./lib/redirect').Redirect
  , Tunnel = require('./lib/tunnel').Tunnel
 
var safeStringify = helpers.safeStringify
  , isReadStream = helpers.isReadStream
  , toBase64 = helpers.toBase64
  , defer = helpers.defer
  , copy = helpers.copy
  , version = helpers.version
  , globalCookieJar = cookies.jar()
 
 
var globalPool = {}
 
function filterForNonReserved(reserved, options) {
  // Filter out properties that are not reserved.
  // Reserved values are passed in at call site.
 
  var object = {}
  for (var i in options) {
    var notReserved = (reserved.indexOf(i) === -1)
    if (notReserved) {
      object[i] = options[i]
    }
  }
  return object
}
 
function filterOutReservedFunctions(reserved, options) {
  // Filter out properties that are functions and are reserved.
  // Reserved values are passed in at call site.
 
  var object = {}
  for (var i in options) {
    var isReserved = !(reserved.indexOf(i) === -1)
    var isFunction = (typeof options[i] === 'function')
    if (!(isReserved && isFunction)) {
      object[i] = options[i]
    }
  }
  return object
 
}
 
// Return a simpler request object to allow serialization
function requestToJSON() {
  var self = this
  return {
    uri: self.uri,
    method: self.method,
    headers: self.headers
  }
}
 
// Return a simpler response object to allow serialization
function responseToJSON() {
  var self = this
  return {
    statusCode: self.statusCode,
    body: self.body,
    headers: self.headers,
    request: requestToJSON.call(self.request)
  }
}
 
function Request (options) {
  // if given the method property in options, set property explicitMethod to true
 
  // extend the Request instance with any non-reserved properties
  // remove any reserved functions from the options object
  // set Request instance to be readable and writable
  // call init
 
  var self = this
 
  // start with HAR, then override with additional options
  if (options.har) {
    self._har = new Har(self)
    options = self._har.options(options)
  }
 
  stream.Stream.call(self)
  var reserved = Object.keys(Request.prototype)
  var nonReserved = filterForNonReserved(reserved, options)
 
  extend(self, nonReserved)
  options = filterOutReservedFunctions(reserved, options)
 
  self.readable = true
  self.writable = true
  if (options.method) {
    self.explicitMethod = true
  }
  self._qs = new Querystring(self)
  self._auth = new Auth(self)
  self._oauth = new OAuth(self)
  self._multipart = new Multipart(self)
  self._redirect = new Redirect(self)
  self._tunnel = new Tunnel(self)
  self.init(options)
}
 
util.inherits(Request, stream.Stream)
 
// Debugging
Request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG)
function debug() {
  if (Request.debug) {
    console.error('REQUEST %s', util.format.apply(util, arguments))
  }
}
Request.prototype.debug = debug
 
Request.prototype.init = function (options) {
  // init() contains all the code to setup the request object.
  // the actual outgoing request is not started until start() is called
  // this function is called from both the constructor and on redirect.
  var self = this
  if (!options) {
    options = {}
  }
  self.headers = self.headers ? copy(self.headers) : {}
 
  // Delete headers with value undefined since they break
  // ClientRequest.OutgoingMessage.setHeader in node 0.12
  for (var headerName in self.headers) {
    if (typeof self.headers[headerName] === 'undefined') {
      delete self.headers[headerName]
    }
  }
 
  caseless.httpify(self, self.headers)
 
  if (!self.method) {
    self.method = options.method || 'GET'
  }
  if (!self.localAddress) {
    self.localAddress = options.localAddress
  }
 
  self._qs.init(options)
 
  debug(options)
  if (!self.pool && self.pool !== false) {
    self.pool = globalPool
  }
  self.dests = self.dests || []
  self.__isRequestRequest = true
 
  // Protect against double callback
  if (!self._callback && self.callback) {
    self._callback = self.callback
    self.callback = function () {
      if (self._callbackCalled) {
        return // Print a warning maybe?
      }
      self._callbackCalled = true
      self._callback.apply(self, arguments)
    }
    self.on('error', self.callback.bind())
    self.on('complete', self.callback.bind(self, null))
  }
 
  // People use this property instead all the time, so support it
  if (!self.uri && self.url) {
    self.uri = self.url
    delete self.url
  }
 
  // If there's a baseUrl, then use it as the base URL (i.e. uri must be
  // specified as a relative path and is appended to baseUrl).
  if (self.baseUrl) {
    if (typeof self.baseUrl !== 'string') {
      return self.emit('error', new Error('options.baseUrl must be a string'))
    }
 
    if (typeof self.uri !== 'string') {
      return self.emit('error', new Error('options.uri must be a string when using options.baseUrl'))
    }
 
    if (self.uri.indexOf('//') === 0 || self.uri.indexOf('://') !== -1) {
      return self.emit('error', new Error('options.uri must be a path when using options.baseUrl'))
    }
 
    // Handle all cases to make sure that there's only one slash between
    // baseUrl and uri.
    var baseUrlEndsWithSlash = self.baseUrl.lastIndexOf('/') === self.baseUrl.length - 1
    var uriStartsWithSlash = self.uri.indexOf('/') === 0
 
    if (baseUrlEndsWithSlash && uriStartsWithSlash) {
      self.uri = self.baseUrl + self.uri.slice(1)
    } else if (baseUrlEndsWithSlash || uriStartsWithSlash) {
      self.uri = self.baseUrl + self.uri
    } else if (self.uri === '') {
      self.uri = self.baseUrl
    } else {
      self.uri = self.baseUrl + '/' + self.uri
    }
    delete self.baseUrl
  }
 
  // A URI is needed by this point, emit error if we haven't been able to get one
  if (!self.uri) {
    return self.emit('error', new Error('options.uri is a required argument'))
  }
 
  // If a string URI/URL was given, parse it into a URL object
  if (typeof self.uri === 'string') {
    self.uri = url.parse(self.uri)
  }
 
  // Some URL objects are not from a URL parsed string and need href added
  if (!self.uri.href) {
    self.uri.href = url.format(self.uri)
  }
 
  // DEPRECATED: Warning for users of the old Unix Sockets URL Scheme
  if (self.uri.protocol === 'unix:') {
    return self.emit('error', new Error('`unix://` URL scheme is no longer supported. Please use the format `http://unix:SOCKET:PATH`'))
  }
 
  // Support Unix Sockets
  if (self.uri.host === 'unix') {
    self.enableUnixSocket()
  }
 
  if (self.strictSSL === false) {
    self.rejectUnauthorized = false
  }
 
  if (!self.uri.pathname) {self.uri.pathname = '/'}
 
  if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) {
    // Invalid URI: it may generate lot of bad errors, like 'TypeError: Cannot call method `indexOf` of undefined' in CookieJar
    // Detect and reject it as soon as possible
    var faultyUri = url.format(self.uri)
    var message = 'Invalid URI "' + faultyUri + '"'
    if (Object.keys(options).length === 0) {
      // No option ? This can be the sign of a redirect
      // As this is a case where the user cannot do anything (they didn't call request directly with this URL)
      // they should be warned that it can be caused by a redirection (can save some hair)
      message += '. This can be caused by a crappy redirection.'
    }
    // This error was fatal
    self.abort()
    return self.emit('error', new Error(message))
  }
 
  if (!self.hasOwnProperty('proxy')) {
    self.proxy = getProxyFromURI(self.uri)
  }
 
  self.tunnel = self._tunnel.isEnabled()
  if (self.proxy) {
    self._tunnel.setup(options)
  }
 
  self._redirect.onRequest(options)
 
  self.setHost = false
  if (!self.hasHeader('host')) {
    var hostHeaderName = self.originalHostHeaderName || 'host'
    self.setHeader(hostHeaderName, self.uri.hostname)
    if (self.uri.port) {
      if ( !(self.uri.port === 80 && self.uri.protocol === 'http:') &&
           !(self.uri.port === 443 && self.uri.protocol === 'https:') ) {
        self.setHeader(hostHeaderName, self.getHeader('host') + (':' + self.uri.port) )
      }
    }
    self.setHost = true
  }
 
  self.jar(self._jar || options.jar)
 
  if (!self.uri.port) {
    if (self.uri.protocol === 'http:') {self.uri.port = 80}
    else if (self.uri.protocol === 'https:') {self.uri.port = 443}
  }
 
  if (self.proxy && !self.tunnel) {
    self.port = self.proxy.port
    self.host = self.proxy.hostname
  } else {
    self.port = self.uri.port
    self.host = self.uri.hostname
  }
 
  if (options.form) {
    self.form(options.form)
  }
 
  if (options.formData) {
    var formData = options.formData
    var requestForm = self.form()
    var appendFormValue = function (key, value) {
      if (value && value.hasOwnProperty('value') && value.hasOwnProperty('options')) {
        requestForm.append(key, value.value, value.options)
      } else {
        requestForm.append(key, value)
      }
    }
    for (var formKey in formData) {
      if (formData.hasOwnProperty(formKey)) {
        var formValue = formData[formKey]
        if (formValue instanceof Array) {
          for (var j = 0; j < formValue.length; j++) {
            appendFormValue(formKey, formValue[j])
          }
        } else {
          appendFormValue(formKey, formValue)
        }
      }
    }
  }
 
  if (options.qs) {
    self.qs(options.qs)
  }
 
  if (self.uri.path) {
    self.path = self.uri.path
  } else {
    self.path = self.uri.pathname + (self.uri.search || '')
  }
 
  if (self.path.length === 0) {
    self.path = '/'
  }
 
  // Auth must happen last in case signing is dependent on other headers
  if (options.aws) {
    self.aws(options.aws)
  }
 
  if (options.hawk) {
    self.hawk(options.hawk)
  }
 
  if (options.httpSignature) {
    self.httpSignature(options.httpSignature)
  }
 
  if (options.auth) {
    if (Object.prototype.hasOwnProperty.call(options.auth, 'username')) {
      options.auth.user = options.auth.username
    }
    if (Object.prototype.hasOwnProperty.call(options.auth, 'password')) {
      options.auth.pass = options.auth.password
    }
 
    self.auth(
      options.auth.user,
      options.auth.pass,
      options.auth.sendImmediately,
      options.auth.bearer
    )
  }
 
  if (self.gzip && !self.hasHeader('accept-encoding')) {
    self.setHeader('accept-encoding', 'gzip, deflate')
  }
 
  if (self.uri.auth && !self.hasHeader('authorization')) {
    var uriAuthPieces = self.uri.auth.split(':').map(function(item) {return self._qs.unescape(item)})
    self.auth(uriAuthPieces[0], uriAuthPieces.slice(1).join(':'), true)
  }
 
  if (!self.tunnel && self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization')) {
    var proxyAuthPieces = self.proxy.auth.split(':').map(function(item) {return self._qs.unescape(item)})
    var authHeader = 'Basic ' + toBase64(proxyAuthPieces.join(':'))
    self.setHeader('proxy-authorization', authHeader)
  }
 
  if (self.proxy && !self.tunnel) {
    self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
  }
 
  if (options.json) {
    self.json(options.json)
  }
  if (options.multipart) {
    self.multipart(options.multipart)
  }
 
  if (options.time) {
    self.timing = true
    self.elapsedTime = self.elapsedTime || 0
  }
 
  function setContentLength () {
    if (isTypedArray(self.body)) {
      self.body = new Buffer(self.body)
    }
 
    if (!self.hasHeader('content-length')) {
      var length
      if (typeof self.body === 'string') {
        length = Buffer.byteLength(self.body)
      }
      else if (Array.isArray(self.body)) {
        length = self.body.reduce(function (a, b) {return a + b.length}, 0)
      }
      else {
        length = self.body.length
      }
 
      if (length) {
        self.setHeader('content-length', length)
      } else {
        self.emit('error', new Error('Argument error, options.body.'))
      }
    }
  }
  if (self.body && !isstream(self.body)) {
    setContentLength()
  }
 
  if (options.oauth) {
    self.oauth(options.oauth)
  } else if (self._oauth.params && self.hasHeader('authorization')) {
    self.oauth(self._oauth.params)
  }
 
  var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol
    , defaultModules = {'http:':http, 'https:':https}
    , httpModules = self.httpModules || {}
 
  self.httpModule = httpModules[protocol] || defaultModules[protocol]
 
  if (!self.httpModule) {
    return self.emit('error', new Error('Invalid protocol: ' + protocol))
  }
 
  if (options.ca) {
    self.ca = options.ca
  }
 
  if (!self.agent) {
    if (options.agentOptions) {
      self.agentOptions = options.agentOptions
    }
 
    if (options.agentClass) {
      self.agentClass = options.agentClass
    } else if (options.forever) {
      var v = version()
      // use ForeverAgent in node 0.10- only
      if (v.major === 0 && v.minor <= 10) {
        self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL
      } else {
        self.agentClass = self.httpModule.Agent
        self.agentOptions = self.agentOptions || {}
        self.agentOptions.keepAlive = true
      }
    } else {
      self.agentClass = self.httpModule.Agent
    }
  }
 
  if (self.pool === false) {
    self.agent = false
  } else {
    self.agent = self.agent || self.getNewAgent()
  }
 
  self.on('pipe', function (src) {
    if (self.ntick && self._started) {
      self.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'))
    }
    self.src = src
    if (isReadStream(src)) {
      if (!self.hasHeader('content-type')) {
        self.setHeader('content-type', mime.lookup(src.path))
      }
    } else {
      if (src.headers) {
        for (var i in src.headers) {
          if (!self.hasHeader(i)) {
            self.setHeader(i, src.headers[i])
          }
        }
      }
      if (self._json && !self.hasHeader('content-type')) {
        self.setHeader('content-type', 'application/json')
      }
      if (src.method && !self.explicitMethod) {
        self.method = src.method
      }
    }
 
    // self.on('pipe', function () {
    //   console.error('You have already piped to this stream. Pipeing twice is likely to break the request.')
    // })
  })
 
  defer(function () {
    if (self._aborted) {
      return
    }
 
    var end = function () {
      if (self._form) {
        if (!self._auth.hasAuth) {
          self._form.pipe(self)
        }
        else if (self._auth.hasAuth && self._auth.sentAuth) {
          self._form.pipe(self)
        }
      }
      if (self._multipart && self._multipart.chunked) {
        self._multipart.body.pipe(self)
      }
      if (self.body) {
        if (isstream(self.body)) {
          self.body.pipe(self)
        } else {
          setContentLength()
          if (Array.isArray(self.body)) {
            self.body.forEach(function (part) {
              self.write(part)
            })
          } else {
            self.write(self.body)
          }
          self.end()
        }
      } else if (self.requestBodyStream) {
        console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.')
        self.requestBodyStream.pipe(self)
      } else if (!self.src) {
        if (self._auth.hasAuth && !self._auth.sentAuth) {
          self.end()
          return
        }
        if (self.method !== 'GET' && typeof self.method !== 'undefined') {
          self.setHeader('content-length', 0)
        }
        self.end()
      }
    }
 
    if (self._form && !self.hasHeader('content-length')) {
      // Before ending the request, we had to compute the length of the whole form, asyncly
      self.setHeader(self._form.getHeaders(), true)
      self._form.getLength(function (err, length) {
        if (!err && !isNaN(length)) {
          self.setHeader('content-length', length)
        }
        end()
      })
    } else {
      end()
    }
 
    self.ntick = true
  })
 
}
 
Request.prototype.getNewAgent = function () {
  var self = this
  var Agent = self.agentClass
  var options = {}
  if (self.agentOptions) {
    for (var i in self.agentOptions) {
      options[i] = self.agentOptions[i]
    }
  }
  if (self.ca) {
    options.ca = self.ca
  }
  if (self.ciphers) {
    options.ciphers = self.ciphers
  }
  if (self.secureProtocol) {
    options.secureProtocol = self.secureProtocol
  }
  if (self.secureOptions) {
    options.secureOptions = self.secureOptions
  }
  if (typeof self.rejectUnauthorized !== 'undefined') {
    options.rejectUnauthorized = self.rejectUnauthorized
  }
 
  if (self.cert && self.key) {
    options.key = self.key
    options.cert = self.cert
  }
 
  if (self.pfx) {
    options.pfx = self.pfx
  }
 
  if (self.passphrase) {
    options.passphrase = self.passphrase
  }
 
  var poolKey = ''
 
  // different types of agents are in different pools
  if (Agent !== self.httpModule.Agent) {
    poolKey += Agent.name
  }
 
  // ca option is only relevant if proxy or destination are https
  var proxy = self.proxy
  if (typeof proxy === 'string') {
    proxy = url.parse(proxy)
  }
  var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
 
  if (isHttps) {
    if (options.ca) {
      if (poolKey) {
        poolKey += ':'
      }
      poolKey += options.ca
    }
 
    if (typeof options.rejectUnauthorized !== 'undefined') {
      if (poolKey) {
        poolKey += ':'
      }
      poolKey += options.rejectUnauthorized
    }
 
    if (options.cert) {
      if (poolKey) {
        poolKey += ':'
      }
      poolKey += options.cert.toString('ascii') + options.key.toString('ascii')
    }
 
    if (options.pfx) {
      if (poolKey) {
        poolKey += ':'
      }
      poolKey += options.pfx.toString('ascii')
    }
 
    if (options.ciphers) {
      if (poolKey) {
        poolKey += ':'
      }
      poolKey += options.ciphers
    }
 
    if (options.secureProtocol) {
      if (poolKey) {
        poolKey += ':'
      }
      poolKey += options.secureProtocol
    }
 
    if (options.secureOptions) {
      if (poolKey) {
        poolKey += ':'
      }
      poolKey += options.secureOptions
    }
  }
 
  if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) {
    // not doing anything special.  Use the globalAgent
    return self.httpModule.globalAgent
  }
 
  // we're using a stored agent.  Make sure it's protocol-specific
  poolKey = self.uri.protocol + poolKey
 
  // generate a new agent for this setting if none yet exists
  if (!self.pool[poolKey]) {
    self.pool[poolKey] = new Agent(options)
    // properly set maxSockets on new agents
    if (self.pool.maxSockets) {
      self.pool[poolKey].maxSockets = self.pool.maxSockets
    }
  }
 
  return self.pool[poolKey]
}
 
Request.prototype.start = function () {
  // start() is called once we are ready to send the outgoing HTTP request.
  // this is usually called on the first write(), end() or on nextTick()
  var self = this
 
  if (self._aborted) {
    return
  }
 
  self._started = true
  self.method = self.method || 'GET'
  self.href = self.uri.href
 
  if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) {
    self.setHeader('content-length', self.src.stat.size)
  }
  if (self._aws) {
    self.aws(self._aws, true)
  }
 
  // We have a method named auth, which is completely different from the http.request
  // auth option.  If we don't remove it, we're gonna have a bad time.
  var reqOptions = copy(self)
  delete reqOptions.auth
 
  debug('make request', self.uri.href)
 
  try {
    self.req = self.httpModule.request(reqOptions)
  } catch (err) {
    self.emit('error', err)
    return
  }
 
  if (self.timing) {
    self.startTime = new Date().getTime()
  }
 
  if (self.timeout && !self.timeoutTimer) {
    var timeout = self.timeout < 0 ? 0 : self.timeout
    // Set a timeout in memory - this block will throw if the server takes more
    // than `timeout` to write the HTTP status and headers (corresponding to
    // the on('response') event on the client). NB: this measures wall-clock
    // time, not the time between bytes sent by the server.
    self.timeoutTimer = setTimeout(function () {
      var connectTimeout = self.req.socket && self.req.socket.readable === false
      self.abort()
      var e = new Error('ETIMEDOUT')
      e.code = 'ETIMEDOUT'
      e.connect = connectTimeout
      self.emit('error', e)
    }, timeout)
 
    if (self.req.setTimeout) { // only works on node 0.6+
      // Set an additional timeout on the socket, via the `setsockopt` syscall.
      // This timeout sets the amount of time to wait *between* bytes sent
      // from the server, and may or may not correspond to the wall-clock time
      // elapsed from the start of the request.
      //
      // In particular, it's useful for erroring if the server fails to send
      // data halfway through streaming a response.
      self.req.setTimeout(timeout, function () {
        if (self.req) {
          self.req.abort()
          var e = new Error('ESOCKETTIMEDOUT')
          e.code = 'ESOCKETTIMEDOUT'
          e.connect = false
          self.emit('error', e)
        }
      })
    }
  }
 
  self.req.on('response', self.onRequestResponse.bind(self))
  self.req.on('error', self.onRequestError.bind(self))
  self.req.on('drain', function() {
    self.emit('drain')
  })
  self.req.on('socket', function(socket) {
    self.emit('socket', socket)
  })
 
  self.emit('request', self.req)
}
 
Request.prototype.onRequestError = function (error) {
  var self = this
  if (self._aborted) {
    return
  }
  if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET'
      && self.agent.addRequestNoreuse) {
    self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
    self.start()
    self.req.end()
    return
  }
  if (self.timeout && self.timeoutTimer) {
    clearTimeout(self.timeoutTimer)
    self.timeoutTimer = null
  }
  self.emit('error', error)
}
 
Request.prototype.onRequestResponse = function (response) {
  var self = this
  debug('onRequestResponse', self.uri.href, response.statusCode, response.headers)
  response.on('end', function() {
    if (self.timing) {
      self.elapsedTime += (new Date().getTime() - self.startTime)
      debug('elapsed time', self.elapsedTime)
      response.elapsedTime = self.elapsedTime
    }
    debug('response end', self.uri.href, response.statusCode, response.headers)
  })
 
  if (self._aborted) {
    debug('aborted', self.uri.href)
    response.resume()
    return
  }
 
  self.response = response
  response.request = self
  response.toJSON = responseToJSON
 
  // XXX This is different on 0.10, because SSL is strict by default
  if (self.httpModule === https &&
      self.strictSSL && (!response.hasOwnProperty('socket') ||
      !response.socket.authorized)) {
    debug('strict ssl error', self.uri.href)
    var sslErr = response.hasOwnProperty('socket') ? response.socket.authorizationError : self.uri.href + ' does not support SSL'
    self.emit('error', new Error('SSL Error: ' + sslErr))
    return
  }
 
  // Save the original host before any redirect (if it changes, we need to
  // remove any authorization headers).  Also remember the case of the header
  // name because lots of broken servers expect Host instead of host and we
  // want the caller to be able to specify this.
  self.originalHost = self.getHeader('host')
  if (!self.originalHostHeaderName) {
    self.originalHostHeaderName = self.hasHeader('host')
  }
  if (self.setHost) {
    self.removeHeader('host')
  }
  if (self.timeout && self.timeoutTimer) {
    clearTimeout(self.timeoutTimer)
    self.timeoutTimer = null
  }
 
  var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar
  var addCookie = function (cookie) {
    //set the cookie if it's domain in the href's domain.
    try {
      targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true})
    } catch (e) {
      self.emit('error', e)
    }
  }
 
  response.caseless = caseless(response.headers)
 
  if (response.caseless.has('set-cookie') && (!self._disableCookies)) {
    var headerName = response.caseless.has('set-cookie')
    if (Array.isArray(response.headers[headerName])) {
      response.headers[headerName].forEach(addCookie)
    } else {
      addCookie(response.headers[headerName])
    }
  }
 
  if (self._redirect.onResponse(response)) {
    return // Ignore the rest of the response
  } else {
    // Be a good stream and emit end when the response is finished.
    // Hack to emit end on close because of a core bug that never fires end
    response.on('close', function () {
      if (!self._ended) {
        self.response.emit('end')
      }
    })
 
    response.on('end', function () {
      self._ended = true
    })
 
    var noBody = function (code) {
      return (
        self.method === 'HEAD'
        // Informational
        || (code >= 100 && code < 200)
        // No Content
        || code === 204
        // Not Modified
        || code === 304
      )
    }
 
    var responseContent
    if (self.gzip && !noBody(response.statusCode)) {
      var contentEncoding = response.headers['content-encoding'] || 'identity'
      contentEncoding = contentEncoding.trim().toLowerCase()
 
      if (contentEncoding === 'gzip') {
        responseContent = zlib.createGunzip()
        response.pipe(responseContent)
      } else if (contentEncoding === 'deflate') {
        responseContent = zlib.createInflate()
        response.pipe(responseContent)
      } else {
        // Since previous versions didn't check for Content-Encoding header,
        // ignore any invalid values to preserve backwards-compatibility
        if (contentEncoding !== 'identity') {
          debug('ignoring unrecognized Content-Encoding ' + contentEncoding)
        }
        responseContent = response
      }
    } else {
      responseContent = response
    }
 
    if (self.encoding) {
      if (self.dests.length !== 0) {
        console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.')
      } else if (responseContent.setEncoding) {
        responseContent.setEncoding(self.encoding)
      } else {
        // Should only occur on node pre-v0.9.4 (joyent/node@9b5abe5) with
        // zlib streams.
        // If/When support for 0.9.4 is dropped, this should be unnecessary.
        responseContent = responseContent.pipe(stringstream(self.encoding))
      }
    }
 
    if (self._paused) {
      responseContent.pause()
    }
 
    self.responseContent = responseContent
 
    self.emit('response', response)
 
    self.dests.forEach(function (dest) {
      self.pipeDest(dest)
    })
 
    responseContent.on('data', function (chunk) {
      self._destdata = true
      self.emit('data', chunk)
    })
    responseContent.on('end', function (chunk) {
      self.emit('end', chunk)
    })
    responseContent.on('error', function (error) {
      self.emit('error', error)
    })
    responseContent.on('close', function () {self.emit('close')})
 
    if (self.callback) {
      self.readResponseBody(response)
    }
    //if no callback
    else {
      self.on('end', function () {
        if (self._aborted) {
          debug('aborted', self.uri.href)
          return
        }
        self.emit('complete', response)
      })
    }
  }
  debug('finish init function', self.uri.href)
}
 
Request.prototype.readResponseBody = function (response) {
  var self = this
  debug('reading response\'s body')
  var buffer = bl()
    , strings = []
 
  self.on('data', function (chunk) {
    if (Buffer.isBuffer(chunk)) {
      buffer.append(chunk)
    } else {
      strings.push(chunk)
    }
  })
  self.on('end', function () {
    debug('end event', self.uri.href)
    if (self._aborted) {
      debug('aborted', self.uri.href)
      // `buffer` is defined in the parent scope and used in a closure it exists for the life of the request.
      // This can lead to leaky behavior if the user retains a reference to the request object.
      buffer.destroy()
      return
    }
 
    if (buffer.length) {
      debug('has body', self.uri.href, buffer.length)
      if (self.encoding === null) {
        // response.body = buffer
        // can't move to this until https://github.com/rvagg/bl/issues/13
        response.body = buffer.slice()
      } else {
        response.body = buffer.toString(self.encoding)
      }
      // `buffer` is defined in the parent scope and used in a closure it exists for the life of the Request.
      // This can lead to leaky behavior if the user retains a reference to the request object.
      buffer.destroy()
    } else if (strings.length) {
      // The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation.
      // Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse().
      if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') {
        strings[0] = strings[0].substring(1)
      }
      response.body = strings.join('')
    }
 
    if (self._json) {
      try {
        response.body = JSON.parse(response.body, self._jsonReviver)
      } catch (e) {
        debug('invalid JSON received', self.uri.href)
      }
    }
    debug('emitting complete', self.uri.href)
    if (typeof response.body === 'undefined' && !self._json) {
      response.body = self.encoding === null ? new Buffer(0) : ''
    }
    self.emit('complete', response, response.body)
  })
}
 
Request.prototype.abort = function () {
  var self = this
  self._aborted = true
 
  if (self.req) {
    self.req.abort()
  }
  else if (self.response) {
    self.response.destroy()
  }
 
  self.emit('abort')
}
 
Request.prototype.pipeDest = function (dest) {
  var self = this
  var response = self.response
  // Called after the response is received
  if (dest.headers && !dest.headersSent) {
    if (response.caseless.has('content-type')) {
      var ctname = response.caseless.has('content-type')
      if (dest.setHeader) {
        dest.setHeader(ctname, response.headers[ctname])
      }
      else {
        dest.headers[ctname] = response.headers[ctname]
      }
    }
 
    if (response.caseless.has('content-length')) {
      var clname = response.caseless.has('content-length')
      if (dest.setHeader) {
        dest.setHeader(clname, response.headers[clname])
      } else {
        dest.headers[clname] = response.headers[clname]
      }
    }
  }
  if (dest.setHeader && !dest.headersSent) {
    for (var i in response.headers) {
      // If the response content is being decoded, the Content-Encoding header
      // of the response doesn't represent the piped content, so don't pass it.
      if (!self.gzip || i !== 'content-encoding') {
        dest.setHeader(i, response.headers[i])
      }
    }
    dest.statusCode = response.statusCode
  }
  if (self.pipefilter) {
    self.pipefilter(response, dest)
  }
}
 
Request.prototype.qs = function (q, clobber) {
  var self = this
  var base
  if (!clobber && self.uri.query) {
    base = self._qs.parse(self.uri.query)
  } else {
    base = {}
  }
 
  for (var i in q) {
    base[i] = q[i]
  }
 
  var qs = self._qs.stringify(base)
 
  if (qs === '') {
    return self
  }
 
  self.uri = url.parse(self.uri.href.split('?')[0] + '?' + qs)
  self.url = self.uri
  self.path = self.uri.path
 
  if (self.uri.host === 'unix') {
    self.enableUnixSocket()
  }
 
  return self
}
Request.prototype.form = function (form) {
  var self = this
  if (form) {
    if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
      self.setHeader('content-type', 'application/x-www-form-urlencoded')
    }
    self.body = (typeof form === 'string')
      ? self._qs.rfc3986(form.toString('utf8'))
      : self._qs.stringify(form).toString('utf8')
    return self
  }
  // create form-data object
  self._form = new FormData()
  self._form.on('error', function(err) {
    err.message = 'form-data: ' + err.message
    self.emit('error', err)
    self.abort()
  })
  return self._form
}
Request.prototype.multipart = function (multipart) {
  var self = this
 
  self._multipart.onRequest(multipart)
 
  if (!self._multipart.chunked) {
    self.body = self._multipart.body
  }
 
  return self
}
Request.prototype.json = function (val) {
  var self = this
 
  if (!self.hasHeader('accept')) {
    self.setHeader('accept', 'application/json')
  }
 
  if (typeof self.jsonReplacer === 'function') {
    self._jsonReplacer = self.jsonReplacer
  }
 
  self._json = true
  if (typeof val === 'boolean') {
    if (self.body !== undefined) {
      if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
        self.body = safeStringify(self.body, self._jsonReplacer)
      } else {
        self.body = self._qs.rfc3986(self.body)
      }
      if (!self.hasHeader('content-type')) {
        self.setHeader('content-type', 'application/json')
      }
    }
  } else {
    self.body = safeStringify(val, self._jsonReplacer)
    if (!self.hasHeader('content-type')) {
      self.setHeader('content-type', 'application/json')
    }
  }
 
  if (typeof self.jsonReviver === 'function') {
    self._jsonReviver = self.jsonReviver
  }
 
  return self
}
Request.prototype.getHeader = function (name, headers) {
  var self = this
  var result, re, match
  if (!headers) {
    headers = self.headers
  }
  Object.keys(headers).forEach(function (key) {
    if (key.length !== name.length) {
      return
    }
    re = new RegExp(name, 'i')
    match = key.match(re)
    if (match) {
      result = headers[key]
    }
  })
  return result
}
Request.prototype.enableUnixSocket = function () {
  // Get the socket & request paths from the URL
  var unixParts = this.uri.path.split(':')
    , host = unixParts[0]
    , path = unixParts[1]
  // Apply unix properties to request
  this.socketPath = host
  this.uri.pathname = path
  this.uri.path = path
  this.uri.host = host
  this.uri.hostname = host
  this.uri.isUnix = true
}
 
 
Request.prototype.auth = function (user, pass, sendImmediately, bearer) {
  var self = this
 
  self._auth.onRequest(user, pass, sendImmediately, bearer)
 
  return self
}
Request.prototype.aws = function (opts, now) {
  var self = this
 
  if (!now) {
    self._aws = opts
    return self
  }
 
  if (opts.sign_version == 4 || opts.sign_version == '4') {
    // use aws4
    var options = {
      host: self.uri.host,
      path: self.uri.path,
      method: self.method,
      headers: {
        'content-type': self.getHeader('content-type') || ''
      },
      body: self.body
    }
    var signRes = aws4.sign(options, {
      accessKeyId: opts.key,
      secretAccessKey: opts.secret
    })
    self.setHeader('authorization', signRes.headers.Authorization)
    self.setHeader('x-amz-date', signRes.headers['X-Amz-Date'])
  }
  else {
    // default: use aws-sign2
    var date = new Date()
    self.setHeader('date', date.toUTCString())
    var auth =
      { key: opts.key
      , secret: opts.secret
      , verb: self.method.toUpperCase()
      , date: date
      , contentType: self.getHeader('content-type') || ''
      , md5: self.getHeader('content-md5') || ''
      , amazonHeaders: aws2.canonicalizeHeaders(self.headers)
      }
    var path = self.uri.path
    if (opts.bucket && path) {
      auth.resource = '/' + opts.bucket + path
    } else if (opts.bucket && !path) {
      auth.resource = '/' + opts.bucket
    } else if (!opts.bucket && path) {
      auth.resource = path
    } else if (!opts.bucket && !path) {
      auth.resource = '/'
    }
    auth.resource = aws2.canonicalizeResource(auth.resource)
    self.setHeader('authorization', aws2.authorization(auth))
  }
 
  return self
}
Request.prototype.httpSignature = function (opts) {
  var self = this
  httpSignature.signRequest({
    getHeader: function(header) {
      return self.getHeader(header, self.headers)
    },
    setHeader: function(header, value) {
      self.setHeader(header, value)
    },
    method: self.method,
    path: self.path
  }, opts)
  debug('httpSignature authorization', self.getHeader('authorization'))
 
  return self
}
Request.prototype.hawk = function (opts) {
  var self = this
  self.setHeader('Authorization', hawk.client.header(self.uri, self.method, opts).field)
}
Request.prototype.oauth = function (_oauth) {
  var self = this
 
  self._oauth.onRequest(_oauth)
 
  return self
}
 
Request.prototype.jar = function (jar) {
  var self = this
  var cookies
 
  if (self._redirect.redirectsFollowed === 0) {
    self.originalCookieHeader = self.getHeader('cookie')
  }
 
  if (!jar) {
    // disable cookies
    cookies = false
    self._disableCookies = true
  } else {
    var targetCookieJar = (jar && jar.getCookieString) ? jar : globalCookieJar
    var urihref = self.uri.href
    //fetch cookie in the Specified host
    if (targetCookieJar) {
      cookies = targetCookieJar.getCookieString(urihref)
    }
  }
 
  //if need cookie and cookie is not empty
  if (cookies && cookies.length) {
    if (self.originalCookieHeader) {
      // Don't overwrite existing Cookie header
      self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies)
    } else {
      self.setHeader('cookie', cookies)
    }
  }
  self._jar = jar
  return self
}
 
 
// Stream API
Request.prototype.pipe = function (dest, opts) {
  var self = this
 
  if (self.response) {
    if (self._destdata) {
      self.emit('error', new Error('You cannot pipe after data has been emitted from the response.'))
    } else if (self._ended) {
      self.emit('error', new Error('You cannot pipe after the response has been ended.'))
    } else {
      stream.Stream.prototype.pipe.call(self, dest, opts)
      self.pipeDest(dest)
      return dest
    }
  } else {
    self.dests.push(dest)
    stream.Stream.prototype.pipe.call(self, dest, opts)
    return dest
  }
}
Request.prototype.write = function () {
  var self = this
  if (self._aborted) {return}
 
  if (!self._started) {
    self.start()
  }
  if (self.req) {
    return self.req.write.apply(self.req, arguments)
  }
}
Request.prototype.end = function (chunk) {
  var self = this
  if (self._aborted) {return}
 
  if (chunk) {
    self.write(chunk)
  }
  if (!self._started) {
    self.start()
  }
  if (self.req) {
    self.req.end()
  }
}
Request.prototype.pause = function () {
  var self = this
  if (!self.responseContent) {
    self._paused = true
  } else {
    self.responseContent.pause.apply(self.responseContent, arguments)
  }
}
Request.prototype.resume = function () {
  var self = this
  if (!self.responseContent) {
    self._paused = false
  } else {
    self.responseContent.resume.apply(self.responseContent, arguments)
  }
}
Request.prototype.destroy = function () {
  var self = this
  if (!self._ended) {
    self.end()
  } else if (self.response) {
    self.response.destroy()
  }
}
 
Request.defaultProxyHeaderWhiteList =
  Tunnel.defaultProxyHeaderWhiteList.slice()
 
Request.defaultProxyHeaderExclusiveList =
  Tunnel.defaultProxyHeaderExclusiveList.slice()
 
// Exports
 
Request.prototype.toJSON = requestToJSON
module.exports = Request
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/

Statements: 17.44% (101 / 579)      Branches: 0.25% (1 / 397)      Functions: 3.95% (3 / 76)      Lines: 17.57% (101 / 575)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/
File Statements Branches Functions Lines
auth.js 10.71% (9 / 84) 0% (0 / 59) 0% (0 / 7) 10.84% (9 / 83)
cookies.js 50% (11 / 22) 0% (0 / 8) 33.33% (2 / 6) 50% (11 / 22)
getProxyFromURI.js 20% (5 / 25) 0% (0 / 31) 0% (0 / 5) 20% (5 / 25)
har.js 10.89% (11 / 101) 0% (0 / 84) 0% (0 / 10) 10.89% (11 / 101)
helpers.js 55.26% (21 / 38) 8.33% (1 / 12) 10% (1 / 10) 55.26% (21 / 38)
multipart.js 13.11% (8 / 61) 0% (0 / 38) 0% (0 / 9) 13.33% (8 / 60)
oauth.js 9.72% (7 / 72) 0% (0 / 57) 0% (0 / 7) 9.72% (7 / 72)
querystring.js 34.78% (8 / 23) 0% (0 / 20) 0% (0 / 6) 36.36% (8 / 22)
redirect.js 8.24% (7 / 85) 0% (0 / 62) 0% (0 / 5) 8.33% (7 / 84)
tunnel.js 20.59% (14 / 68) 0% (0 / 26) 0% (0 / 11) 20.59% (14 / 68)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/auth.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/auth.js

Statements: 10.71% (9 / 84)      Branches: 0% (0 / 59)      Functions: 0% (0 / 7)      Lines: 10.84% (9 / 83)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170    1       1       1                   1                               1                           1                                                                                                                                                       1                                 1                                               1    
'use strict'
 
var caseless = require('caseless')
  , uuid = require('node-uuid')
  , helpers = require('./helpers')
 
var md5 = helpers.md5
  , toBase64 = helpers.toBase64
 
 
function Auth (request) {
  // define all public properties here
  this.request = request
  this.hasAuth = false
  this.sentAuth = false
  this.bearerToken = null
  this.user = null
  this.pass = null
}
 
Auth.prototype.basic = function (user, pass, sendImmediately) {
  var self = this
  if (typeof user !== 'string' || (pass !== undefined && typeof pass !== 'string')) {
    self.request.emit('error', new Error('auth() received invalid user or password'))
  }
  self.user = user
  self.pass = pass
  self.hasAuth = true
  var header = user + ':' + (pass || '')
  if (sendImmediately || typeof sendImmediately === 'undefined') {
    var authHeader = 'Basic ' + toBase64(header)
    self.sentAuth = true
    return authHeader
  }
}
 
Auth.prototype.bearer = function (bearer, sendImmediately) {
  var self = this
  self.bearerToken = bearer
  self.hasAuth = true
  if (sendImmediately || typeof sendImmediately === 'undefined') {
    if (typeof bearer === 'function') {
      bearer = bearer()
    }
    var authHeader = 'Bearer ' + (bearer || '')
    self.sentAuth = true
    return authHeader
  }
}
 
Auth.prototype.digest = function (method, path, authHeader) {
  // TODO: More complete implementation of RFC 2617.
  //   - handle challenge.domain
  //   - support qop="auth-int" only
  //   - handle Authentication-Info (not necessarily?)
  //   - check challenge.stale (not necessarily?)
  //   - increase nc (not necessarily?)
  // For reference:
  // http://tools.ietf.org/html/rfc2617#section-3
  // https://github.com/bagder/curl/blob/master/lib/http_digest.c
 
  var self = this
 
  var challenge = {}
  var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
  for (;;) {
    var match = re.exec(authHeader)
    if (!match) {
      break
    }
    challenge[match[1]] = match[2] || match[3]
  }
 
  /**
   * RFC 2617: handle both MD5 and MD5-sess algorithms.
   *
   * If the algorithm directive's value is "MD5" or unspecified, then HA1 is
   *   HA1=MD5(username:realm:password)
   * If the algorithm directive's value is "MD5-sess", then HA1 is
   *   HA1=MD5(MD5(username:realm:password):nonce:cnonce)
   */
  var ha1Compute = function (algorithm, user, realm, pass, nonce, cnonce) {
    var ha1 = md5(user + ':' + realm + ':' + pass)
    if (algorithm && algorithm.toLowerCase() === 'md5-sess') {
      return md5(ha1 + ':' + nonce + ':' + cnonce)
    } else {
      return ha1
    }
  }
 
  var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth'
  var nc = qop && '00000001'
  var cnonce = qop && uuid().replace(/-/g, '')
  var ha1 = ha1Compute(challenge.algorithm, self.user, challenge.realm, self.pass, challenge.nonce, cnonce)
  var ha2 = md5(method + ':' + path)
  var digestResponse = qop
    ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
    : md5(ha1 + ':' + challenge.nonce + ':' + ha2)
  var authValues = {
    username: self.user,
    realm: challenge.realm,
    nonce: challenge.nonce,
    uri: path,
    qop: qop,
    response: digestResponse,
    nc: nc,
    cnonce: cnonce,
    algorithm: challenge.algorithm,
    opaque: challenge.opaque
  }
 
  authHeader = []
  for (var k in authValues) {
    if (authValues[k]) {
      if (k === 'qop' || k === 'nc' || k === 'algorithm') {
        authHeader.push(k + '=' + authValues[k])
      } else {
        authHeader.push(k + '="' + authValues[k] + '"')
      }
    }
  }
  authHeader = 'Digest ' + authHeader.join(', ')
  self.sentAuth = true
  return authHeader
}
 
Auth.prototype.onRequest = function (user, pass, sendImmediately, bearer) {
  var self = this
    , request = self.request
 
  var authHeader
  if (bearer === undefined && user === undefined) {
    self.request.emit('error', new Error('no auth mechanism defined'))
  } else if (bearer !== undefined) {
    authHeader = self.bearer(bearer, sendImmediately)
  } else {
    authHeader = self.basic(user, pass, sendImmediately)
  }
  if (authHeader) {
    request.setHeader('authorization', authHeader)
  }
}
 
Auth.prototype.onResponse = function (response) {
  var self = this
    , request = self.request
 
  if (!self.hasAuth || self.sentAuth) { return null }
 
  var c = caseless(response.headers)
 
  var authHeader = c.get('www-authenticate')
  var authVerb = authHeader && authHeader.split(' ')[0].toLowerCase()
  request.debug('reauth', authVerb)
 
  switch (authVerb) {
    case 'basic':
      return self.basic(self.user, self.pass, true)
 
    case 'bearer':
      return self.bearer(self.bearerToken, true)
 
    case 'digest':
      return self.digest(request.method, request.path, authHeader)
  }
}
 
exports.Auth = Auth
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/cookies.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/cookies.js

Statements: 50% (11 / 22)      Branches: 0% (0 / 8)      Functions: 33.33% (2 / 6)      Lines: 50% (11 / 22)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41    1   1       1                     1 1 1   1       1       1         1 1      
'use strict'
 
var tough = require('tough-cookie')
 
var Cookie = tough.Cookie
  , CookieJar = tough.CookieJar
 
 
exports.parse = function(str) {
  if (str && str.uri) {
    str = str.uri
  }
  if (typeof str !== 'string') {
    throw new Error('The cookie function only accepts STRING as param')
  }
  return Cookie.parse(str, {loose: true})
}
 
// Adapt the sometimes-Async api of tough.CookieJar to our requirements
function RequestJar(store) {
  var self = this
  self._jar = new CookieJar(store, {looseMode: true})
}
RequestJar.prototype.setCookie = function(cookieOrStr, uri, options) {
  var self = this
  return self._jar.setCookieSync(cookieOrStr, uri, options || {})
}
RequestJar.prototype.getCookieString = function(uri) {
  var self = this
  return self._jar.getCookieStringSync(uri)
}
RequestJar.prototype.getCookies = function(uri) {
  var self = this
  return self._jar.getCookiesSync(uri)
}
 
exports.jar = function(store) {
  return new RequestJar(store)
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/getProxyFromURI.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/getProxyFromURI.js

Statements: 20% (5 / 25)      Branches: 0% (0 / 31)      Functions: 0% (0 / 5)      Lines: 20% (5 / 25)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81    1         1                     1                                         1                                                                             1    
'use strict'
 
function formatHostname(hostname) {
  // canonicalize the hostname, so that 'oogle.com' won't match 'google.com'
  return hostname.replace(/^\.*/, '.').toLowerCase()
}
 
function parseNoProxyZone(zone) {
  zone = zone.trim().toLowerCase()
 
  var zoneParts = zone.split(':', 2)
    , zoneHost = formatHostname(zoneParts[0])
    , zonePort = zoneParts[1]
    , hasPort = zone.indexOf(':') > -1
 
  return {hostname: zoneHost, port: zonePort, hasPort: hasPort}
}
 
function uriInNoProxy(uri, noProxy) {
  var port = uri.port || (uri.protocol === 'https:' ? '443' : '80')
    , hostname = formatHostname(uri.hostname)
    , noProxyList = noProxy.split(',')
 
  // iterate through the noProxyList until it finds a match.
  return noProxyList.map(parseNoProxyZone).some(function(noProxyZone) {
    var isMatchedAt = hostname.indexOf(noProxyZone.hostname)
      , hostnameMatched = (
          isMatchedAt > -1 &&
          (isMatchedAt === hostname.length - noProxyZone.hostname.length)
        )
 
    if (noProxyZone.hasPort) {
      return (port === noProxyZone.port) && hostnameMatched
    }
 
    return hostnameMatched
  })
}
 
function getProxyFromURI(uri) {
  // Decide the proper request proxy to use based on the request URI object and the
  // environmental variables (NO_PROXY, HTTP_PROXY, etc.)
  // respect NO_PROXY environment variables (see: http://lynx.isc.org/current/breakout/lynx_help/keystrokes/environments.html)
 
  var noProxy = process.env.NO_PROXY || process.env.no_proxy || ''
 
  // if the noProxy is a wildcard then return null
 
  if (noProxy === '*') {
    return null
  }
 
  // if the noProxy is not empty and the uri is found return null
 
  if (noProxy !== '' && uriInNoProxy(uri, noProxy)) {
    return null
  }
 
  // Check for HTTP or HTTPS Proxy in environment Else default to null
 
  if (uri.protocol === 'http:') {
    return process.env.HTTP_PROXY ||
           process.env.http_proxy || null
  }
 
  if (uri.protocol === 'https:') {
    return process.env.HTTPS_PROXY ||
           process.env.https_proxy ||
           process.env.HTTP_PROXY  ||
           process.env.http_proxy  || null
  }
 
  // if none of that works, return null
  // (What uri protocol are you using then?)
 
  return null
}
 
module.exports = getProxyFromURI
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/har.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/har.js

Statements: 10.89% (11 / 101)      Branches: 0% (0 / 84)      Functions: 0% (0 / 10)      Lines: 10.89% (11 / 101)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217    1 1 1 1   1       1                                   1                                                                 1                                                                                                       1                                                                                                     1                                                                                                 1    
'use strict'
 
var fs = require('fs')
var qs = require('querystring')
var validate = require('har-validator')
var extend = require('extend')
 
function Har (request) {
  this.request = request
}
 
Har.prototype.reducer = function (obj, pair) {
  // new property ?
  if (obj[pair.name] === undefined) {
    obj[pair.name] = pair.value
    return obj
  }
 
  // existing? convert to array
  var arr = [
    obj[pair.name],
    pair.value
  ]
 
  obj[pair.name] = arr
 
  return obj
}
 
Har.prototype.prep = function (data) {
  // construct utility properties
  data.queryObj = {}
  data.headersObj = {}
  data.postData.jsonObj = false
  data.postData.paramsObj = false
 
  // construct query objects
  if (data.queryString && data.queryString.length) {
    data.queryObj = data.queryString.reduce(this.reducer, {})
  }
 
  // construct headers objects
  if (data.headers && data.headers.length) {
    // loweCase header keys
    data.headersObj = data.headers.reduceRight(function (headers, header) {
      headers[header.name] = header.value
      return headers
    }, {})
  }
 
  // construct Cookie header
  if (data.cookies && data.cookies.length) {
    var cookies = data.cookies.map(function (cookie) {
      return cookie.name + '=' + cookie.value
    })
 
    if (cookies.length) {
      data.headersObj.cookie = cookies.join('; ')
    }
  }
 
  // prep body
  function some (arr) {
    return arr.some(function (type) {
      return data.postData.mimeType.indexOf(type) === 0
    })
  }
 
  if (some([
    'multipart/mixed',
    'multipart/related',
    'multipart/form-data',
    'multipart/alternative'])) {
 
    // reset values
    data.postData.mimeType = 'multipart/form-data'
  }
 
  else if (some([
    'application/x-www-form-urlencoded'])) {
 
    if (!data.postData.params) {
      data.postData.text = ''
    } else {
      data.postData.paramsObj = data.postData.params.reduce(this.reducer, {})
 
      // always overwrite
      data.postData.text = qs.stringify(data.postData.paramsObj)
    }
  }
 
  else if (some([
    'text/json',
    'text/x-json',
    'application/json',
    'application/x-json'])) {
 
    data.postData.mimeType = 'application/json'
 
    if (data.postData.text) {
      try {
        data.postData.jsonObj = JSON.parse(data.postData.text)
      } catch (e) {
        this.request.debug(e)
 
        // force back to text/plain
        data.postData.mimeType = 'text/plain'
      }
    }
  }
 
  return data
}
 
Har.prototype.options = function (options) {
  // skip if no har property defined
  if (!options.har) {
    return options
  }
 
  var har = {}
  extend(har, options.har)
 
  // only process the first entry
  if (har.log && har.log.entries) {
    har = har.log.entries[0]
  }
 
  // add optional properties to make validation successful
  har.url = har.url || options.url || options.uri || options.baseUrl || '/'
  har.httpVersion = har.httpVersion || 'HTTP/1.1'
  har.queryString = har.queryString || []
  har.headers = har.headers || []
  har.cookies = har.cookies || []
  har.postData = har.postData || {}
  har.postData.mimeType = har.postData.mimeType || 'application/octet-stream'
 
  har.bodySize = 0
  har.headersSize = 0
  har.postData.size = 0
 
  if (!validate.request(har)) {
    return options
  }
 
  // clean up and get some utility properties
  var req = this.prep(har)
 
  // construct new options
  if (req.url) {
    options.url = req.url
  }
 
  if (req.method) {
    options.method = req.method
  }
 
  if (Object.keys(req.queryObj).length) {
    options.qs = req.queryObj
  }
 
  if (Object.keys(req.headersObj).length) {
    options.headers = req.headersObj
  }
 
  function test (type) {
    return req.postData.mimeType.indexOf(type) === 0
  }
  if (test('application/x-www-form-urlencoded')) {
    options.form = req.postData.paramsObj
  }
  else if (test('application/json')) {
    if (req.postData.jsonObj) {
      options.body = req.postData.jsonObj
      options.json = true
    }
  }
  else if (test('multipart/form-data')) {
    options.formData = {}
 
    req.postData.params.forEach(function (param) {
      var attachment = {}
 
      if (!param.fileName && !param.fileName && !param.contentType) {
        options.formData[param.name] = param.value
        return
      }
 
      // attempt to read from disk!
      if (param.fileName && !param.value) {
        attachment.value = fs.createReadStream(param.fileName)
      } else if (param.value) {
        attachment.value = param.value
      }
 
      if (param.fileName) {
        attachment.options = {
          filename: param.fileName,
          contentType: param.contentType ? param.contentType : null
        }
      }
 
      options.formData[param.name] = attachment
    })
  }
  else {
    if (req.postData.text) {
      options.body = req.postData.text
    }
  }
 
  return options
}
 
exports.Har = Har
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/helpers.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/helpers.js

Statements: 55.26% (21 / 38)      Branches: 8.33% (1 / 12)      Functions: 10% (1 / 10)      Lines: 55.26% (21 / 38)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76    1     1 1       1     1       1                 1                   1       1       1       1               1                 1 1 1 1 1 1 1 1 1    
'use strict'
 
var jsonSafeStringify = require('json-stringify-safe')
  , crypto = require('crypto')
 
function deferMethod() {
  Iif (typeof setImmediate === 'undefined') {
    return process.nextTick
  }
 
  return setImmediate
}
 
function isFunction(value) {
  return typeof value === 'function'
}
 
function paramsHaveRequestBody(params) {
  return (
    params.body ||
    params.requestBodyStream ||
    (params.json && typeof params.json !== 'boolean') ||
    params.multipart
  )
}
 
function safeStringify (obj, replacer) {
  var ret
  try {
    ret = JSON.stringify(obj, replacer)
  } catch (e) {
    ret = jsonSafeStringify(obj, replacer)
  }
  return ret
}
 
function md5 (str) {
  return crypto.createHash('md5').update(str).digest('hex')
}
 
function isReadStream (rs) {
  return rs.readable && rs.path && rs.mode
}
 
function toBase64 (str) {
  return (new Buffer(str || '', 'utf8')).toString('base64')
}
 
function copy (obj) {
  var o = {}
  Object.keys(obj).forEach(function (i) {
    o[i] = obj[i]
  })
  return o
}
 
function version () {
  var numbers = process.version.replace('v', '').split('.')
  return {
    major: parseInt(numbers[0], 10),
    minor: parseInt(numbers[1], 10),
    patch: parseInt(numbers[2], 10)
  }
}
 
exports.isFunction            = isFunction
exports.paramsHaveRequestBody = paramsHaveRequestBody
exports.safeStringify         = safeStringify
exports.md5                   = md5
exports.isReadStream          = isReadStream
exports.toBase64              = toBase64
exports.copy                  = copy
exports.version               = version
exports.defer                 = deferMethod()
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/multipart.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/multipart.js

Statements: 13.11% (8 / 61)      Branches: 0% (0 / 38)      Functions: 0% (0 / 9)      Lines: 13.33% (8 / 60)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114    1         1             1                                                             1                                       1       1                                                             1                     1    
'use strict'
 
var uuid = require('node-uuid')
  , CombinedStream = require('combined-stream')
  , isstream = require('isstream')
 
 
function Multipart (request) {
  this.request = request
  this.boundary = uuid()
  this.chunked = false
  this.body = null
}
 
Multipart.prototype.isChunked = function (options) {
  var self = this
    , chunked = false
    , parts = options.data || options
 
  if (!parts.forEach) {
    self.request.emit('error', new Error('Argument error, options.multipart.'))
  }
 
  if (options.chunked !== undefined) {
    chunked = options.chunked
  }
 
  if (self.request.getHeader('transfer-encoding') === 'chunked') {
    chunked = true
  }
 
  if (!chunked) {
    parts.forEach(function (part) {
      if (typeof part.body === 'undefined') {
        self.request.emit('error', new Error('Body attribute missing in multipart.'))
      }
      if (isstream(part.body)) {
        chunked = true
      }
    })
  }
 
  return chunked
}
 
Multipart.prototype.setHeaders = function (chunked) {
  var self = this
 
  if (chunked && !self.request.hasHeader('transfer-encoding')) {
    self.request.setHeader('transfer-encoding', 'chunked')
  }
 
  var header = self.request.getHeader('content-type')
 
  if (!header || header.indexOf('multipart') === -1) {
    self.request.setHeader('content-type', 'multipart/related; boundary=' + self.boundary)
  } else {
    if (header.indexOf('boundary') !== -1) {
      self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1')
    } else {
      self.request.setHeader('content-type', header + '; boundary=' + self.boundary)
    }
  }
}
 
Multipart.prototype.build = function (parts, chunked) {
  var self = this
  var body = chunked ? new CombinedStream() : []
 
  function add (part) {
    if (typeof part === 'number') {
      part = part.toString()
    }
    return chunked ? body.append(part) : body.push(new Buffer(part))
  }
 
  if (self.request.preambleCRLF) {
    add('\r\n')
  }
 
  parts.forEach(function (part) {
    var preamble = '--' + self.boundary + '\r\n'
    Object.keys(part).forEach(function (key) {
      if (key === 'body') { return }
      preamble += key + ': ' + part[key] + '\r\n'
    })
    preamble += '\r\n'
    add(preamble)
    add(part.body)
    add('\r\n')
  })
  add('--' + self.boundary + '--')
 
  if (self.request.postambleCRLF) {
    add('\r\n')
  }
 
  return body
}
 
Multipart.prototype.onRequest = function (options) {
  var self = this
 
  var chunked = self.isChunked(options)
    , parts = options.data || options
 
  self.setHeaders(chunked)
  self.chunked = chunked
  self.body = self.build(parts, chunked)
}
 
exports.Multipart = Multipart
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/oauth.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/oauth.js

Statements: 9.72% (7 / 72)      Branches: 0% (0 / 57)      Functions: 0% (0 / 7)      Lines: 9.72% (7 / 72)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149    1               1         1                                                                                             1                         1                                 1                                                                                                           1    
'use strict'
 
var url = require('url')
  , qs = require('qs')
  , caseless = require('caseless')
  , uuid = require('node-uuid')
  , oauth = require('oauth-sign')
  , crypto = require('crypto')
 
 
function OAuth (request) {
  this.request = request
  this.params = null
}
 
OAuth.prototype.buildParams = function (_oauth, uri, method, query, form, qsLib) {
  var oa = {}
  for (var i in _oauth) {
    oa['oauth_' + i] = _oauth[i]
  }
  if (!oa.oauth_version) {
    oa.oauth_version = '1.0'
  }
  if (!oa.oauth_timestamp) {
    oa.oauth_timestamp = Math.floor( Date.now() / 1000 ).toString()
  }
  if (!oa.oauth_nonce) {
    oa.oauth_nonce = uuid().replace(/-/g, '')
  }
  if (!oa.oauth_signature_method) {
    oa.oauth_signature_method = 'HMAC-SHA1'
  }
 
  var consumer_secret_or_private_key = oa.oauth_consumer_secret || oa.oauth_private_key
  delete oa.oauth_consumer_secret
  delete oa.oauth_private_key
 
  var token_secret = oa.oauth_token_secret
  delete oa.oauth_token_secret
 
  var realm = oa.oauth_realm
  delete oa.oauth_realm
  delete oa.oauth_transport_method
 
  var baseurl = uri.protocol + '//' + uri.host + uri.pathname
  var params = qsLib.parse([].concat(query, form, qsLib.stringify(oa)).join('&'))
 
  oa.oauth_signature = oauth.sign(
    oa.oauth_signature_method,
    method,
    baseurl,
    params,
    consumer_secret_or_private_key,
    token_secret)
 
  if (realm) {
    oa.realm = realm
  }
 
  return oa
}
 
OAuth.prototype.buildBodyHash = function(_oauth, body) {
  if (['HMAC-SHA1', 'RSA-SHA1'].indexOf(_oauth.signature_method || 'HMAC-SHA1') < 0) {
    this.request.emit('error', new Error('oauth: ' + _oauth.signature_method +
      ' signature_method not supported with body_hash signing.'))
  }
 
  var shasum = crypto.createHash('sha1')
  shasum.update(body || '')
  var sha1 = shasum.digest('hex')
 
  return new Buffer(sha1).toString('base64')
}
 
OAuth.prototype.concatParams = function (oa, sep, wrap) {
  wrap = wrap || ''
 
  var params = Object.keys(oa).filter(function (i) {
    return i !== 'realm' && i !== 'oauth_signature'
  }).sort()
 
  if (oa.realm) {
    params.splice(0, 0, 'realm')
  }
  params.push('oauth_signature')
 
  return params.map(function (i) {
    return i + '=' + wrap + oauth.rfc3986(oa[i]) + wrap
  }).join(sep)
}
 
OAuth.prototype.onRequest = function (_oauth) {
  var self = this
  self.params = _oauth
 
  var uri = self.request.uri || {}
    , method = self.request.method || ''
    , headers = caseless(self.request.headers)
    , body = self.request.body || ''
    , qsLib = self.request.qsLib || qs
 
  var form
    , query
    , contentType = headers.get('content-type') || ''
    , formContentType = 'application/x-www-form-urlencoded'
    , transport = _oauth.transport_method || 'header'
 
  if (contentType.slice(0, formContentType.length) === formContentType) {
    contentType = formContentType
    form = body
  }
  if (uri.query) {
    query = uri.query
  }
  if (transport === 'body' && (method !== 'POST' || contentType !== formContentType)) {
    self.request.emit('error', new Error('oauth: transport_method of body requires POST ' +
      'and content-type ' + formContentType))
  }
 
  if (!form && typeof _oauth.body_hash === 'boolean') {
    _oauth.body_hash = self.buildBodyHash(_oauth, self.request.body.toString())
  }
 
  var oa = self.buildParams(_oauth, uri, method, query, form, qsLib)
 
  switch (transport) {
    case 'header':
      self.request.setHeader('Authorization', 'OAuth ' + self.concatParams(oa, ',', '"'))
      break
 
    case 'query':
      var href = self.request.uri.href += (query ? '&' : '?') + self.concatParams(oa, '&')
      self.request.uri = url.parse(href)
      self.request.path = self.request.uri.path
      break
 
    case 'body':
      self.request.body = (form ? form + '&' : '') + self.concatParams(oa, '&')
      break
 
    default:
      self.request.emit('error', new Error('oauth: transport_method invalid'))
  }
}
 
exports.OAuth = OAuth
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/querystring.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/querystring.js

Statements: 34.78% (8 / 23)      Branches: 0% (0 / 20)      Functions: 0% (0 / 6)      Lines: 36.36% (8 / 22)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53    1       1               1                   1                 1                 1           1   1    
'use strict'
 
var qs = require('qs')
  , querystring = require('querystring')
 
 
function Querystring (request) {
  this.request = request
  this.lib = null
  this.useQuerystring = null
  this.parseOptions = null
  this.stringifyOptions = null
}
 
Querystring.prototype.init = function (options) {
  if (this.lib) {return}
 
  this.useQuerystring = options.useQuerystring
  this.lib = (this.useQuerystring ? querystring : qs)
 
  this.parseOptions = options.qsParseOptions || {}
  this.stringifyOptions = options.qsStringifyOptions || {}
}
 
Querystring.prototype.stringify = function (obj) {
  return (this.useQuerystring)
    ? this.rfc3986(this.lib.stringify(obj,
      this.stringifyOptions.sep || null,
      this.stringifyOptions.eq || null,
      this.stringifyOptions))
    : this.lib.stringify(obj, this.stringifyOptions)
}
 
Querystring.prototype.parse = function (str) {
  return (this.useQuerystring)
    ? this.lib.parse(str,
      this.parseOptions.sep || null,
      this.parseOptions.eq || null,
      this.parseOptions)
    : this.lib.parse(str, this.parseOptions)
}
 
Querystring.prototype.rfc3986 = function (str) {
  return str.replace(/[!'()*]/g, function (c) {
    return '%' + c.charCodeAt(0).toString(16).toUpperCase()
  })
}
 
Querystring.prototype.unescape = querystring.unescape
 
exports.Querystring = Querystring
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/redirect.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/redirect.js

Statements: 8.24% (7 / 85)      Branches: 0% (0 / 62)      Functions: 0% (0 / 5)      Lines: 8.33% (7 / 84)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155    1 1   1                       1                                             1                                                                   1                                                                                                                                                           1    
'use strict'
 
var url = require('url')
var isUrl = /^https?:/
 
function Redirect (request) {
  this.request = request
  this.followRedirect = true
  this.followRedirects = true
  this.followAllRedirects = false
  this.allowRedirect = function () {return true}
  this.maxRedirects = 10
  this.redirects = []
  this.redirectsFollowed = 0
  this.removeRefererHeader = false
}
 
Redirect.prototype.onRequest = function (options) {
  var self = this
 
  if (options.maxRedirects !== undefined) {
    self.maxRedirects = options.maxRedirects
  }
  if (typeof options.followRedirect === 'function') {
    self.allowRedirect = options.followRedirect
  }
  if (options.followRedirect !== undefined) {
    self.followRedirects = !!options.followRedirect
  }
  if (options.followAllRedirects !== undefined) {
    self.followAllRedirects = options.followAllRedirects
  }
  if (self.followRedirects || self.followAllRedirects) {
    self.redirects = self.redirects || []
  }
  if (options.removeRefererHeader !== undefined) {
    self.removeRefererHeader = options.removeRefererHeader
  }
}
 
Redirect.prototype.redirectTo = function (response) {
  var self = this
    , request = self.request
 
  var redirectTo = null
  if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) {
    var location = response.caseless.get('location')
    request.debug('redirect', location)
 
    if (self.followAllRedirects) {
      redirectTo = location
    } else if (self.followRedirects) {
      switch (request.method) {
        case 'PATCH':
        case 'PUT':
        case 'POST':
        case 'DELETE':
          // Do not follow redirects
          break
        default:
          redirectTo = location
          break
      }
    }
  } else if (response.statusCode === 401) {
    var authHeader = request._auth.onResponse(response)
    if (authHeader) {
      request.setHeader('authorization', authHeader)
      redirectTo = request.uri
    }
  }
  return redirectTo
}
 
Redirect.prototype.onResponse = function (response) {
  var self = this
    , request = self.request
 
  var redirectTo = self.redirectTo(response)
  if (!redirectTo || !self.allowRedirect.call(request, response)) {
    return false
  }
 
  request.debug('redirect to', redirectTo)
 
  // ignore any potential response body.  it cannot possibly be useful
  // to us at this point.
  // response.resume should be defined, but check anyway before calling. Workaround for browserify.
  if (response.resume) {
    response.resume()
  }
 
  if (self.redirectsFollowed >= self.maxRedirects) {
    request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href))
    return false
  }
  self.redirectsFollowed += 1
 
  if (!isUrl.test(redirectTo)) {
    redirectTo = url.resolve(request.uri.href, redirectTo)
  }
 
  var uriPrev = request.uri
  request.uri = url.parse(redirectTo)
 
  // handle the case where we change protocol from https to http or vice versa
  if (request.uri.protocol !== uriPrev.protocol) {
    delete request.agent
  }
 
  self.redirects.push(
    { statusCode : response.statusCode
    , redirectUri: redirectTo
    }
  )
  if (self.followAllRedirects && request.method !== 'HEAD'
    && response.statusCode !== 401 && response.statusCode !== 307) {
    request.method = 'GET'
  }
  // request.method = 'GET' // Force all redirects to use GET || commented out fixes #215
  delete request.src
  delete request.req
  delete request._started
  if (response.statusCode !== 401 && response.statusCode !== 307) {
    // Remove parameters from the previous response, unless this is the second request
    // for a server that requires digest authentication.
    delete request.body
    delete request._form
    if (request.headers) {
      request.removeHeader('host')
      request.removeHeader('content-type')
      request.removeHeader('content-length')
      if (request.uri.hostname !== request.originalHost.split(':')[0]) {
        // Remove authorization if changing hostnames (but not if just
        // changing ports or protocols).  This matches the behavior of curl:
        // https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710
        request.removeHeader('authorization')
      }
    }
  }
 
  if (!self.removeRefererHeader) {
    request.setHeader('referer', uriPrev.href)
  }
 
  request.emit('redirect')
 
  request.init()
 
  return true
}
 
exports.Redirect = Redirect
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/tunnel.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/request/lib/tunnel.js

Statements: 20.59% (14 / 68)      Branches: 0% (0 / 26)      Functions: 0% (0 / 11)      Lines: 20.59% (14 / 68)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178    1     1                                               1       1                               1                                 1                                                 1           1               1                 1                                     1                                                                               1 1 1    
'use strict'
 
var url = require('url')
  , tunnel = require('tunnel-agent')
 
var defaultProxyHeaderWhiteList = [
  'accept',
  'accept-charset',
  'accept-encoding',
  'accept-language',
  'accept-ranges',
  'cache-control',
  'content-encoding',
  'content-language',
  'content-location',
  'content-md5',
  'content-range',
  'content-type',
  'connection',
  'date',
  'expect',
  'max-forwards',
  'pragma',
  'referer',
  'te',
  'user-agent',
  'via'
]
 
var defaultProxyHeaderExclusiveList = [
  'proxy-authorization'
]
 
function constructProxyHost(uriObject) {
  var port = uriObject.port
    , protocol = uriObject.protocol
    , proxyHost = uriObject.hostname + ':'
 
  if (port) {
    proxyHost += port
  } else if (protocol === 'https:') {
    proxyHost += '443'
  } else {
    proxyHost += '80'
  }
 
  return proxyHost
}
 
function constructProxyHeaderWhiteList(headers, proxyHeaderWhiteList) {
  var whiteList = proxyHeaderWhiteList
    .reduce(function (set, header) {
      set[header.toLowerCase()] = true
      return set
    }, {})
 
  return Object.keys(headers)
    .filter(function (header) {
      return whiteList[header.toLowerCase()]
    })
    .reduce(function (set, header) {
      set[header] = headers[header]
      return set
    }, {})
}
 
function constructTunnelOptions (request, proxyHeaders) {
  var proxy = request.proxy
 
  var tunnelOptions = {
    proxy : {
      host      : proxy.hostname,
      port      : +proxy.port,
      proxyAuth : proxy.auth,
      headers   : proxyHeaders
    },
    headers            : request.headers,
    ca                 : request.ca,
    cert               : request.cert,
    key                : request.key,
    passphrase         : request.passphrase,
    pfx                : request.pfx,
    ciphers            : request.ciphers,
    rejectUnauthorized : request.rejectUnauthorized,
    secureOptions      : request.secureOptions,
    secureProtocol     : request.secureProtocol
  }
 
  return tunnelOptions
}
 
function constructTunnelFnName(uri, proxy) {
  var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http')
  var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http')
  return [uriProtocol, proxyProtocol].join('Over')
}
 
function getTunnelFn(request) {
  var uri = request.uri
  var proxy = request.proxy
  var tunnelFnName = constructTunnelFnName(uri, proxy)
  return tunnel[tunnelFnName]
}
 
 
function Tunnel (request) {
  this.request = request
  this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList
  this.proxyHeaderExclusiveList = []
  if (typeof request.tunnel !== 'undefined') {
    this.tunnelOverride = request.tunnel
  }
}
 
Tunnel.prototype.isEnabled = function () {
  var self = this
    , request = self.request
  // Tunnel HTTPS by default. Allow the user to override this setting.
 
  // If self.tunnelOverride is set (the user specified a value), use it.
  if (typeof self.tunnelOverride !== 'undefined') {
    return self.tunnelOverride
  }
 
  // If the destination is HTTPS, tunnel.
  if (request.uri.protocol === 'https:') {
    return true
  }
 
  // Otherwise, do not use tunnel.
  return false
}
 
Tunnel.prototype.setup = function (options) {
  var self = this
    , request = self.request
 
  options = options || {}
 
  if (typeof request.proxy === 'string') {
    request.proxy = url.parse(request.proxy)
  }
 
  if (!request.proxy || !request.tunnel) {
    return false
  }
 
  // Setup Proxy Header Exclusive List and White List
  if (options.proxyHeaderWhiteList) {
    self.proxyHeaderWhiteList = options.proxyHeaderWhiteList
  }
  if (options.proxyHeaderExclusiveList) {
    self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList
  }
 
  var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList)
  var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList)
 
  // Setup Proxy Headers and Proxy Headers Host
  // Only send the Proxy White Listed Header names
  var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList)
  proxyHeaders.host = constructProxyHost(request.uri)
 
  proxyHeaderExclusiveList.forEach(request.removeHeader, request)
 
  // Set Agent from Tunnel Data
  var tunnelFn = getTunnelFn(request)
  var tunnelOptions = constructTunnelOptions(request, proxyHeaders)
  request.agent = tunnelFn(tunnelOptions)
 
  return true
}
 
Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList
Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList
exports.Tunnel = Tunnel
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/tunnel-agent/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/tunnel-agent/

Statements: 19.42% (27 / 139)      Branches: 5.88% (2 / 34)      Functions: 0% (0 / 23)      Lines: 19.57% (27 / 138)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/tunnel-agent/
File Statements Branches Functions Lines
index.js 19.42% (27 / 139) 5.88% (2 / 34) 0% (0 / 23) 19.57% (27 / 138)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/tunnel-agent/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-feedparser/node_modules/tunnel-agent/index.js

Statements: 19.42% (27 / 139)      Branches: 5.88% (2 / 34)      Functions: 0% (0 / 23)      Lines: 19.57% (27 / 138)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245    1                 1 1 1 1     1           1               1           1                 1                                             1   1                                           1                 1       1                 1                                                   1         1             1                                   1                     1                           1                             1                                 1 1                     1   1    
'use strict'
 
var net = require('net')
  , tls = require('tls')
  , http = require('http')
  , https = require('https')
  , events = require('events')
  , assert = require('assert')
  , util = require('util')
  ;
 
exports.httpOverHttp = httpOverHttp
exports.httpsOverHttp = httpsOverHttp
exports.httpOverHttps = httpOverHttps
exports.httpsOverHttps = httpsOverHttps
 
 
function httpOverHttp(options) {
  var agent = new TunnelingAgent(options)
  agent.request = http.request
  return agent
}
 
function httpsOverHttp(options) {
  var agent = new TunnelingAgent(options)
  agent.request = http.request
  agent.createSocket = createSecureSocket
  agent.defaultPort = 443
  return agent
}
 
function httpOverHttps(options) {
  var agent = new TunnelingAgent(options)
  agent.request = https.request
  return agent
}
 
function httpsOverHttps(options) {
  var agent = new TunnelingAgent(options)
  agent.request = https.request
  agent.createSocket = createSecureSocket
  agent.defaultPort = 443
  return agent
}
 
 
function TunnelingAgent(options) {
  var self = this
  self.options = options || {}
  self.proxyOptions = self.options.proxy || {}
  self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets
  self.requests = []
  self.sockets = []
 
  self.on('free', function onFree(socket, host, port) {
    for (var i = 0, len = self.requests.length; i < len; ++i) {
      var pending = self.requests[i]
      if (pending.host === host && pending.port === port) {
        // Detect the request to connect same origin server,
        // reuse the connection.
        self.requests.splice(i, 1)
        pending.request.onSocket(socket)
        return
      }
    }
    socket.destroy()
    self.removeSocket(socket)
  })
}
util.inherits(TunnelingAgent, events.EventEmitter)
 
TunnelingAgent.prototype.addRequest = function addRequest(req, options) {
  var self = this
 
   // Legacy API: addRequest(req, host, port, path)
  if (typeof options === 'string') {
    options = {
      host: options,
      port: arguments[2],
      path: arguments[3]
    };
  }
 
  if (self.sockets.length >= this.maxSockets) {
    // We are over limit so we'll add it to the queue.
    self.requests.push({host: options.host, port: options.port, request: req})
    return
  }
 
  // If we are under maxSockets create a new one.
  self.createConnection({host: options.host, port: options.port, request: req})
}
 
TunnelingAgent.prototype.createConnection = function createConnection(pending) {
  var self = this
 
  self.createSocket(pending, function(socket) {
    socket.on('free', onFree)
    socket.on('close', onCloseOrRemove)
    socket.on('agentRemove', onCloseOrRemove)
    pending.request.onSocket(socket)
 
    function onFree() {
      self.emit('free', socket, pending.host, pending.port)
    }
 
    function onCloseOrRemove(err) {
      self.removeSocket(socket)
      socket.removeListener('free', onFree)
      socket.removeListener('close', onCloseOrRemove)
      socket.removeListener('agentRemove', onCloseOrRemove)
    }
  })
}
 
TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
  var self = this
  var placeholder = {}
  self.sockets.push(placeholder)
 
  var connectOptions = mergeOptions({}, self.proxyOptions, 
    { method: 'CONNECT'
    , path: options.host + ':' + options.port
    , agent: false
    }
  )
  if (connectOptions.proxyAuth) {
    connectOptions.headers = connectOptions.headers || {}
    connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
        new Buffer(connectOptions.proxyAuth).toString('base64')
  }
 
  debug('making CONNECT request')
  var connectReq = self.request(connectOptions)
  connectReq.useChunkedEncodingByDefault = false // for v0.6
  connectReq.once('response', onResponse) // for v0.6
  connectReq.once('upgrade', onUpgrade)   // for v0.6
  connectReq.once('connect', onConnect)   // for v0.7 or later
  connectReq.once('error', onError)
  connectReq.end()
 
  function onResponse(res) {
    // Very hacky. This is necessary to avoid http-parser leaks.
    res.upgrade = true
  }
 
  function onUpgrade(res, socket, head) {
    // Hacky.
    process.nextTick(function() {
      onConnect(res, socket, head)
    })
  }
 
  function onConnect(res, socket, head) {
    connectReq.removeAllListeners()
    socket.removeAllListeners()
 
    if (res.statusCode === 200) {
      assert.equal(head.length, 0)
      debug('tunneling connection has established')
      self.sockets[self.sockets.indexOf(placeholder)] = socket
      cb(socket)
    } else {
      debug('tunneling socket could not be established, statusCode=%d', res.statusCode)
      var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode)
      error.code = 'ECONNRESET'
      options.request.emit('error', error)
      self.removeSocket(placeholder)
    }
  }
 
  function onError(cause) {
    connectReq.removeAllListeners()
 
    debug('tunneling socket could not be established, cause=%s\n', cause.message, cause.stack)
    var error = new Error('tunneling socket could not be established, ' + 'cause=' + cause.message)
    error.code = 'ECONNRESET'
    options.request.emit('error', error)
    self.removeSocket(placeholder)
  }
}
 
TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {
  var pos = this.sockets.indexOf(socket)
  if (pos === -1) return
  
  this.sockets.splice(pos, 1)
 
  var pending = this.requests.shift()
  if (pending) {
    // If we have pending requests and a socket gets closed a new one
    // needs to be created to take over in the pool for the one that closed.
    this.createConnection(pending)
  }
}
 
function createSecureSocket(options, cb) {
  var self = this
  TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {
    // 0 is dummy port for v0.6
    var secureSocket = tls.connect(0, mergeOptions({}, self.options, 
      { servername: options.host
      , socket: socket
      }
    ))
    self.sockets[self.sockets.indexOf(socket)] = secureSocket
    cb(secureSocket)
  })
}
 
 
function mergeOptions(target) {
  for (var i = 1, len = arguments.length; i < len; ++i) {
    var overrides = arguments[i]
    if (typeof overrides === 'object') {
      var keys = Object.keys(overrides)
      for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {
        var k = keys[j]
        if (overrides[k] !== undefined) {
          target[k] = overrides[k]
        }
      }
    }
  }
  return target
}
 
 
var debug
Iif (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
  debug = function() {
    var args = Array.prototype.slice.call(arguments)
    if (typeof args[0] === 'string') {
      args[0] = 'TUNNEL: ' + args[0]
    } else {
      args.unshift('TUNNEL:')
    }
    console.error.apply(console, args)
  }
} else {
  debug = function() {}
}
exports.debug = debug // for test
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-rbe/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-rbe/

Statements: 5.08% (3 / 59)      Branches: 0% (0 / 64)      Functions: 33.33% (1 / 3)      Lines: 6% (3 / 50)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-rbe/
File Statements Branches Functions Lines
rbe.js 5.08% (3 / 59) 0% (0 / 64) 33.33% (1 / 3) 6% (3 / 50)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-rbe/rbe.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-rbe/rbe.js

Statements: 5.08% (3 / 59)      Branches: 0% (0 / 64)      Functions: 33.33% (1 / 3)      Lines: 6% (3 / 50)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78  1   1                                                                                                                                             1      
 
module.exports = function(RED) {
    "use strict";
    function RbeNode(n) {
        RED.nodes.createNode(this,n);
        this.func = n.func || "rbe";
        this.gap = n.gap || "0";
        this.start = n.start || '';
        this.inout = n.inout || "out";
        this.pc = false;
        if (this.gap.substr(-1) === "%") {
            this.pc = true;
            this.gap = parseFloat(this.gap);
        }
        this.g = this.gap;
        var node = this;
 
        node.previous = {};
        this.on("input",function(msg) {
            if (msg.hasOwnProperty("payload")) {
                var t = msg.topic || "_no_topic";
                if (this.func === "rbe") {
                    if (typeof(msg.payload) === "object") {
                        if (typeof(node.previous[t]) !== "object") { node.previous[t] = {}; }
                        if (!RED.util.compareObjects(msg.payload, node.previous[t])) {
                            node.previous[t] = msg.payload;
                            node.send(msg);
                        }
                    }
                    else {
                        if (msg.payload !== node.previous[t]) {
                            node.previous[t] = msg.payload;
                            node.send(msg);
                        }
                    }
                }
                else {
                    var n = parseFloat(msg.payload);
                    if (!isNaN(n)) {
                        if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband")) {
                            if (node.start === '') { node.previous[t] = n; }
                            else { node.previous[t] = node.start; }
                        }
                        if (node.pc) { node.gap = (node.previous[t] * node.g / 100) || 0; }
                        else { node.gap = Number(node.gap); }
                        if ((node.previous[t] === undefined) && (node.func === "narrowbandEq")) { node.previous[t] = n; }
                        if (node.previous[t] === undefined) { node.previous[t] = n - node.gap; }
                        if (Math.abs(n - node.previous[t]) === node.gap) {
                            if (this.func === "deadbandEq") {
                                if (node.inout === "out") { node.previous[t] = n; }
                                node.send(msg);
                            }
                        }
                        else if (Math.abs(n - node.previous[t]) > node.gap) {
                            if (this.func === "deadband") {
                                if (node.inout === "out") { node.previous[t] = n; }
                                node.send(msg);
                            }
                        }
                        else if (Math.abs(n - node.previous[t]) < node.gap) {
                            if ((this.func === "narrowband")||(this.func === "narrowbandEq")) {
                                if (node.inout === "out") { node.previous[t] = n; }
                                node.send(msg);
                            }
                        }
                        if (node.inout === "in") { node.previous[t] = n; }
                    }
                    else {
                        node.warn(RED._("rbe.warn.nonumber"));
                    }
                }
            } // ignore msg with no payload property.
        });
    }
    RED.nodes.registerType("rbe",RbeNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-twitter/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-twitter/

Statements: 5.19% (15 / 289)      Branches: 0% (0 / 152)      Functions: 2.44% (1 / 41)      Lines: 5.47% (15 / 274)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red-node-twitter/
File Statements Branches Functions Lines
27-twitter.js 5.19% (15 / 289) 0% (0 / 152) 2.44% (1 / 41) 5.47% (15 / 274)
Code coverage report for node-npmtest-node-red/node_modules/node-red-node-twitter/27-twitter.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red-node-twitter/27-twitter.js

Statements: 5.19% (15 / 289)      Branches: 0% (0 / 152)      Functions: 2.44% (1 / 41)      Lines: 5.47% (15 / 274)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507  1   1 1 1 1   1       1                       1                                             1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     1     1                                                                                                                                       1   1                   1                                     1                                                    
 
module.exports = function(RED) {
    "use strict";
    var Ntwitter = require('twitter-ng');
    var OAuth= require('oauth').OAuth;
    var request = require('request');
    var twitterRateTimeout;
 
    function TwitterNode(n) {
        RED.nodes.createNode(this,n);
        this.screen_name = n.screen_name;
    }
    RED.nodes.registerType("twitter-credentials",TwitterNode,{
        credentials: {
            screen_name: {type:"text"},
            access_token: {type: "password"},
            access_token_secret: {type:"password"}
        }
    });
 
 
    /**
     * Populate msg.location based on data found in msg.tweet.
     */
    function addLocationToTweet(msg) {
        if (msg.tweet) {
            if (msg.tweet.geo) { // if geo is set, always set location from geo
                if (msg.tweet.geo.coordinates && msg.tweet.geo.coordinates.length === 2) {
                    if (!msg.location) { msg.location = {}; }
                    // coordinates[0] is lat, coordinates[1] is lon
                    msg.location.lat = msg.tweet.geo.coordinates[0];
                    msg.location.lon = msg.tweet.geo.coordinates[1];
                    msg.location.icon = "twitter";
                }
            }
            else if (msg.tweet.coordinates) { // otherwise attempt go get it from coordinates
                if (msg.tweet.coordinates.coordinates && msg.tweet.coordinates.coordinates.length === 2) {
                    if (!msg.location) { msg.location = {}; }
                    // WARNING! coordinates[1] is lat, coordinates[0] is lon!!!
                    msg.location.lat = msg.tweet.coordinates.coordinates[1];
                    msg.location.lon = msg.tweet.coordinates.coordinates[0];
                    msg.location.icon = "twitter";
                }
            } // if none of these found then just do nothing
        } // if no msg.tweet then just do nothing
    }
 
    function TwitterInNode(n) {
        RED.nodes.createNode(this,n);
        this.active = true;
        this.user = n.user;
        //this.tags = n.tags.replace(/ /g,'');
        this.tags = n.tags;
        this.twitter = n.twitter;
        this.topic = n.topic||"tweets";
        this.twitterConfig = RED.nodes.getNode(this.twitter);
        var credentials = RED.nodes.getCredentials(this.twitter);
 
        if (credentials && credentials.screen_name == this.twitterConfig.screen_name) {
            var twit = new Ntwitter({
                consumer_key: "OKjYEd1ef2bfFolV25G5nQ",
                consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
                access_token_key: credentials.access_token,
                access_token_secret: credentials.access_token_secret
            });
 
            //setInterval(function() {
            //        twit.get("/application/rate_limit_status.json",null,function(err,cb) {
            //                console.log("direct_messages:",cb["resources"]["direct_messages"]);
            //        });
            //
            //},10000);
 
            var node = this;
            if (this.user === "user") {
                node.poll_ids = [];
                node.since_ids = {};
                node.status({});
                var users = node.tags.split(",");
                if (users === '') { node.warn(RED._("twitter.warn.nousers")); }
                //if (users.length === 0) { node.warn(RED._("twitter.warn.nousers")); }
                else {
                    for (var i=0; i<users.length; i++) {
                        var user = users[i].replace(" ","");
                        twit.getUserTimeline({
                            screen_name:user,
                            trim_user:0,
                            count:1
                        },(function() {
                            var u = user+"";
                            return function(err,cb) {
                                if (err) {
                                    node.error(err);
                                    return;
                                }
                                if (cb[0]) {
                                    node.since_ids[u] = cb[0].id_str;
                                }
                                else {
                                    node.since_ids[u] = '0';
                                }
                                node.poll_ids.push(setInterval(function() {
                                    twit.getUserTimeline({
                                        screen_name:u,
                                        trim_user:0,
                                        since_id:node.since_ids[u]
                                    }, function(err,cb) {
                                        if (cb) {
                                            for (var t=cb.length-1; t>=0; t-=1) {
                                                var tweet = cb[t];
                                                var where = tweet.user.location;
                                                var la = tweet.lang || tweet.user.lang;
                                                var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, lang:la, tweet:tweet };
                                                if (where) {
                                                    msg.location = {place:where};
                                                    addLocationToTweet(msg);
                                                }
                                                node.send(msg);
                                                if (t === 0) {
                                                    node.since_ids[u] = tweet.id_str;
                                                }
                                            }
                                        }
                                        if (err) {
                                            node.error(err);
                                        }
                                    });
                                },60000));
                            }
                        }()));
                    }
                }
            }
            else if (this.user === "dm") {
                node.poll_ids = [];
                node.status({});
                twit.getDirectMessages({
                    screen_name:node.twitterConfig.screen_name,
                    trim_user:0,
                    count:1
                },function(err,cb) {
                    if (err) {
                        node.error(err);
                        return;
                    }
                    if (cb[0]) {
                        node.since_id = cb[0].id_str;
                    }
                    else {
                        node.since_id = '0';
                    }
                    node.poll_ids.push(setInterval(function() {
                        twit.getDirectMessages({
                            screen_name:node.twitterConfig.screen_name,
                            trim_user:0,
                            since_id:node.since_id
                        },function(err,cb) {
                                if (cb) {
                                    for (var t=cb.length-1;t>=0;t-=1) {
                                        var tweet = cb[t];
                                        var where = tweet.sender.location;
                                        var la = tweet.lang || tweet.sender.lang;
                                        var msg = { topic:node.topic+"/"+tweet.sender.screen_name, payload:tweet.text, lang:la, tweet:tweet };
                                        if (where) {
                                            msg.location = {place:where};
                                            addLocationToTweet(msg);
                                        }
                                        node.send(msg);
                                        if (t === 0) {
                                            node.since_id = tweet.id_str;
                                        }
                                    }
                                }
                                if (err) {
                                    node.error(err);
                                }
                            });
                    },120000));
                });
            }
            else if (this.user === "event") {
                try {
                    var thingu = 'user';
                    var setupEvStream = function() {
                        if (node.active) {
                            twit.stream(thingu, st, function(stream) {
                                node.status({fill:"green", shape:"dot", text:" "});
                                node.stream = stream;
                                stream.on('data', function(tweet) {
                                    if (tweet.event !== undefined) {
                                        var where = tweet.source.location;
                                        var la = tweet.source.lang;
                                        var msg = { topic:node.topic+"/"+tweet.source.screen_name, payload:tweet.event, lang:la, tweet:tweet };
                                        if (where) {
                                            msg.location = {place:where};
                                            addLocationToTweet(msg);
                                        }
                                        node.send(msg);
                                    }
                                });
                                stream.on('limit', function(tweet) {
                                    node.status({fill:"grey", shape:"dot", text:" "});
                                    node.tout2 = setTimeout(function() { node.status({fill:"green", shape:"dot", text:" "}); },10000);
                                });
                                stream.on('error', function(tweet,rc) {
                                    //console.log("ERRO",rc,tweet);
                                    if (rc == 420) {
                                        node.status({fill:"red", shape:"ring", text:RED._("twitter.errors.ratelimit")});
                                    }
                                    else {
                                        node.status({fill:"red", shape:"ring", text:" "});
                                        node.warn(RED._("twitter.errors.streamerror",{error:tweet.toString(),rc:rc}));
                                    }
                                    twitterRateTimeout = Date.now() + retry;
                                    if (node.restart) {
                                        node.tout = setTimeout(function() { setupEvStream() },retry);
                                    }
                                });
                                stream.on('destroy', function (response) {
                                    //console.log("DEST",response)
                                    twitterRateTimeout = Date.now() + 15000;
                                    if (node.restart) {
                                        node.status({fill:"red", shape:"dot", text:" "});
                                        node.warn(RED._("twitter.errors.unexpectedend"));
                                        node.tout = setTimeout(function() { setupEvStream() },15000);
                                    }
                                });
                            });
                        }
                    }
                    setupEvStream();
                }
                catch (err) {
                    node.error(err);
                }
            }
            else {
                try {
                    var thing = 'statuses/filter';
                    var tags = node.tags;
                    var st = { track: [tags] };
 
                    var setupStream = function() {
                        if (node.restart) {
                            node.status({fill:"green", shape:"dot", text:(tags||" ")});
                            twit.stream(thing, st, function(stream) {
                                //console.log("ST",st);
                                node.stream = stream;
                                var retry = 60000; // 60 secs backoff for now
                                stream.on('data', function(tweet) {
                                    if (tweet.user !== undefined) {
                                        var where = tweet.user.location;
                                        var la = tweet.lang || tweet.user.lang;
                                        var msg = { topic:node.topic+"/"+tweet.user.screen_name, payload:tweet.text, lang:la, tweet:tweet };
                                        if (where) {
                                            msg.location = {place:where};
                                            addLocationToTweet(msg);
                                        }
                                        node.send(msg);
                                        //node.status({fill:"green", shape:"dot", text:(tags||" ")});
                                    }
                                });
                                stream.on('limit', function(tweet) {
                                    //node.status({fill:"grey", shape:"dot", text:RED._("twitter.errors.limitrate")});
                                    node.status({fill:"grey", shape:"dot", text:(tags||" ")});
                                    node.tout2 = setTimeout(function() { node.status({fill:"green", shape:"dot", text:(tags||" ")}); },10000);
                                });
                                stream.on('error', function(tweet,rc) {
                                    //console.log("ERRO",rc,tweet);
                                    if (rc == 420) {
                                        node.status({fill:"red", shape:"ring", text:RED._("twitter.errors.ratelimit")});
                                    }
                                    else {
                                        node.status({fill:"red", shape:"ring", text:tweet.toString()});
                                        node.warn(RED._("twitter.errors.streamerror",{error:tweet.toString(),rc:rc}));
                                    }
                                    twitterRateTimeout = Date.now() + retry;
                                    if (node.restart) {
                                        node.tout = setTimeout(function() { setupStream() },retry);
                                    }
                                });
                                stream.on('destroy', function (response) {
                                    //console.log("DEST",response)
                                    twitterRateTimeout = Date.now() + 15000;
                                    if (node.restart) {
                                        node.status({fill:"red", shape:"dot", text:" "});
                                        node.warn(RED._("twitter.errors.unexpectedend"));
                                        node.tout = setTimeout(function() { setupStream() },15000);
                                    }
                                });
                            });
                        }
                    }
 
                    // ask for users stream instead of public
                    if (this.user === "true") {
                        thing = 'user';
                        // twit.getFriendsIds(node.twitterConfig.screen_name.substr(1), function(err,list) {
                        //     friends = list;
                        // });
                        st = null;
                    }
 
                    // if 4 numeric tags that look like a geo area then set geo area
                    var bits = node.tags.split(",");
                    if (bits.length == 4) {
                        if ((Number(bits[0]) < Number(bits[2])) && (Number(bits[1]) < Number(bits[3]))) {
                            st = { locations: node.tags };
                            node.log(RED._("twitter.status.using-geo",{location:node.tags.toString()}));
                        }
                    }
 
                    // all public tweets
                    if (this.user === "false") {
                        node.on("input", function(msg) {
                            if (this.tags === '') {
                                if (node.tout) { clearTimeout(node.tout); }
                                if (node.tout2) { clearTimeout(node.tout2); }
                                if (this.stream) {
                                    this.restart = false;
                                    node.stream.removeAllListeners();
                                    this.stream.destroy();
                                }
                                if ((typeof msg.payload === "string") && (msg.payload !== "")) {
                                    st = { track:[msg.payload] };
                                    tags = msg.payload;
 
                                    this.restart = true;
                                    if ((twitterRateTimeout - Date.now()) > 0 ) {
                                        node.status({fill:"red", shape:"ring", text:tags});
                                        node.tout = setTimeout(function() {
                                            setupStream();
                                        }, twitterRateTimeout - Date.now() );
                                    }
                                    else {
                                        setupStream();
                                    }
                                }
                                else {
                                    node.status({fill:"yellow", shape:"ring", text:RED._("twitter.warn.waiting")});
                                }
                            }
                        });
                    }
 
                    // wait for input or start the stream
                    if ((this.user === "false") && (tags === '')) {
                        node.status({fill:"yellow", shape:"ring", text:RED._("twitter.warn.waiting")});
                    }
                    else {
                        this.restart = true;
                        setupStream();
                    }
                }
                catch (err) {
                    node.error(err);
                }
            }
        }
        else {
            this.error(RED._("twitter.errors.missingcredentials"));
        }
 
        this.on('close', function() {
            if (node.tout) { clearTimeout(node.tout); }
            if (node.tout2) { clearTimeout(node.tout2); }
            if (this.stream) {
                this.restart = false;
                node.stream.removeAllListeners();
                this.stream.destroy();
            }
            if (this.poll_ids) {
                for (var i=0;i<this.poll_ids.length;i++) {
                    clearInterval(this.poll_ids[i]);
                }
            }
        });
    }
    RED.nodes.registerType("twitter in",TwitterInNode);
 
 
    function TwitterOutNode(n) {
        RED.nodes.createNode(this,n);
        this.topic = n.topic;
        this.twitter = n.twitter;
        this.twitterConfig = RED.nodes.getNode(this.twitter);
        var credentials = RED.nodes.getCredentials(this.twitter);
        var node = this;
 
        if (credentials && credentials.screen_name == this.twitterConfig.screen_name) {
            var twit = new Ntwitter({
                consumer_key: "OKjYEd1ef2bfFolV25G5nQ",
                consumer_secret: "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
                access_token_key: credentials.access_token,
                access_token_secret: credentials.access_token_secret
            });
            node.on("input", function(msg) {
                if (msg.hasOwnProperty("payload")) {
                    node.status({fill:"blue",shape:"dot",text:"twitter.status.tweeting"});
 
                    if (msg.payload.length > 140) {
                        msg.payload = msg.payload.slice(0,139);
                        node.warn(RED._("twitter.errors.truncated"));
                    }
 
                    if (msg.media && Buffer.isBuffer(msg.media)) {
                        var apiUrl = "https://api.twitter.com/1.1/statuses/update_with_media.json";
                        var signedUrl = oa.signUrl(apiUrl,
                            credentials.access_token,
                            credentials.access_token_secret,
                            "POST");
 
                        var r = request.post(signedUrl,function(err,httpResponse,body) {
                            if (err) {
                                node.error(err,msg);
                                node.status({fill:"red",shape:"ring",text:"twitter.status.failed"});
                            }
                            else {
                                var response = JSON.parse(body);
                                if (response.errors) {
                                    var errorList = response.errors.map(function(er) { return er.code+": "+er.message }).join(", ");
                                    node.error(RED._("twitter.errors.sendfail",{error:errorList}),msg);
                                    node.status({fill:"red",shape:"ring",text:"twitter.status.failed"});
                                }
                                else {
                                    node.status({});
                                }
                            }
                        });
                        var form = r.form();
                        form.append("status",msg.payload);
                        form.append("media[]",msg.media,{filename:"image"});
 
                    }
                    else {
                        if (typeof msg.params === 'undefined') { msg.params = {}; }
                        twit.updateStatus(msg.payload, msg.params, function (err, data) {
                            if (err) {
                                node.status({fill:"red",shape:"ring",text:"twitter.status.failed"});
                                node.error(err,msg);
                            }
                            node.status({});
                        });
                    }
                }
                else { node.warn(RED._("twitter.errors.nopayload")); }
            });
        }
    }
    RED.nodes.registerType("twitter out",TwitterOutNode);
 
    var oa = new OAuth(
        "https://api.twitter.com/oauth/request_token",
        "https://api.twitter.com/oauth/access_token",
        "OKjYEd1ef2bfFolV25G5nQ",
        "meRsltCktVMUI8gmggpXett7WBLd1k0qidYazoML6g",
        "1.0",
        null,
        "HMAC-SHA1"
    );
 
    RED.httpAdmin.get('/twitter-credentials/:id/auth', function(req, res) {
        var credentials = {};
        oa.getOAuthRequestToken({
            oauth_callback: req.query.callback
        },function(error, oauth_token, oauth_token_secret, results) {
            if (error) {
                var err = {statusCode: 401, data: "dummy error"};
                var resp = RED._("twitter.errors.oautherror",{statusCode: err.statusCode, errorData: err.data});
                res.send(resp)
            }
            else {
                credentials.oauth_token = oauth_token;
                credentials.oauth_token_secret = oauth_token_secret;
                res.redirect('https://api.twitter.com/oauth/authorize?oauth_token='+oauth_token)
                RED.nodes.addCredentials(req.params.id,credentials);
            }
        });
    });
 
    RED.httpAdmin.get('/twitter-credentials/:id/auth/callback', function(req, res, next) {
        var credentials = RED.nodes.getCredentials(req.params.id);
        credentials.oauth_verifier = req.query.oauth_verifier;
 
        oa.getOAuthAccessToken(
            credentials.oauth_token,
            credentials.token_secret,
            credentials.oauth_verifier,
            function(error, oauth_access_token, oauth_access_token_secret, results) {
                if (error) {
                    RED.log.error(error);
                    res.send(RED._("twitter.errors.oauthbroke"));
                }
                else {
                    credentials = {};
                    credentials.access_token = oauth_access_token;
                    credentials.access_token_secret = oauth_access_token_secret;
                    credentials.screen_name = "@"+results.screen_name;
                    RED.nodes.addCredentials(req.params.id,credentials);
                    res.send(RED._("twitter.errors.authorized"));
                }
            }
        );
    });
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/

Statements: 44.83% (78 / 174)      Branches: 41.8% (51 / 122)      Functions: 26.67% (4 / 15)      Lines: 45.03% (77 / 171)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/
File Statements Branches Functions Lines
red.js 44.51% (77 / 173) 41.67% (50 / 120) 26.67% (4 / 15) 44.71% (76 / 170)
settings.js 100% (1 / 1) 50% (1 / 2) 100% (0 / 0) 100% (1 / 1)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red.js

Statements: 44.51% (77 / 173)      Branches: 41.67% (50 / 120)      Functions: 26.67% (4 / 15)      Lines: 44.71% (76 / 170)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309                                1 1 1 1 1 1   1 1 1 1   1 1   1 1   1               1                   1       1   1                                 1       1     1       1       1 1 1   1                               1 1 1                         1       1     1   1   1 2     2     2     1       1 1     1 1 1         1 1 1     1 1   1     1       1 1                                   1                                                     1         1 1   1     1 1   1               1 1     1 1       1     1 1 1 1 1 1                 1   1                                     1                   1              
//#!/usr/bin/env node
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
var http = require('http');
var https = require('https');
var util = require("util");
var express = require("express");
var crypto = require("crypto");
try { bcrypt = require('bcrypt'); }
catch(e) { bcrypt = require('bcryptjs'); }
var nopt = require("nopt");
var path = require("path");
var fs = require("fs-extra");
var RED = require("./red/red.js");
 
var server;
var app = express();
 
var settingsFile;
var flowFile;
 
var knownOpts = {
    "help": Boolean,
    "port": Number,
    "settings": [path],
    "title": String,
    "userDir": [path],
    "verbose": Boolean
};
var shortHands = {
    "?":["--help"],
    "p":["--port"],
    "s":["--settings"],
    // As we want to reserve -t for now, adding a shorthand to help so it
    // doesn't get treated as --title
    "t":["--help"],
    "u":["--userDir"],
    "v":["--verbose"]
};
nopt.invalidHandler = function(k,v,t) {
    // TODO: console.log(k,v,t);
}
 
var parsedArgs = nopt(knownOpts,shortHands,process.argv,2)
 
Iif (parsedArgs.help) {
    console.log("Node-RED v"+RED.version());
    console.log("Usage: node-red [-v] [-?] [--settings settings.js] [--userDir DIR]");
    console.log("                [--port PORT] [--title TITLE] [flows.json]");
    console.log("");
    console.log("Options:");
    console.log("  -p, --port     PORT  port to listen on");
    console.log("  -s, --settings FILE  use specified settings file");
    console.log("      --title    TITLE process window title");
    console.log("  -u, --userDir  DIR   use specified user directory");
    console.log("  -v, --verbose        enable verbose output");
    console.log("  -?, --help           show this help");
    console.log("");
    console.log("Documentation can be found at http://nodered.org");
    process.exit();
}
 
Iif (parsedArgs.argv.remain.length > 0) {
    flowFile = parsedArgs.argv.remain[0];
}
 
Iif (parsedArgs.settings) {
    // User-specified settings file
    settingsFile = parsedArgs.settings;
} else Iif (parsedArgs.userDir && fs.existsSync(path.join(parsedArgs.userDir,"settings.js"))) {
    // User-specified userDir that contains a settings.js
    settingsFile = path.join(parsedArgs.userDir,"settings.js");
} else {
    Iif (fs.existsSync(path.join(process.env.NODE_RED_HOME,".config.json"))) {
        // NODE_RED_HOME contains user data - use its settings.js
        settingsFile = path.join(process.env.NODE_RED_HOME,"settings.js");
    } else {
        var userDir = parsedArgs.userDir || path.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE,".node-red");
        var userSettingsFile = path.join(userDir,"settings.js");
        Eif (fs.existsSync(userSettingsFile)) {
            // $HOME/.node-red/settings.js exists
            settingsFile = userSettingsFile;
        } else {
            var defaultSettings = path.join(__dirname,"settings.js");
            var settingsStat = fs.statSync(defaultSettings);
            if (settingsStat.mtime.getTime() <= settingsStat.ctime.getTime()) {
                // Default settings file has not been modified - safe to copy
                fs.copySync(defaultSettings,userSettingsFile);
                settingsFile = userSettingsFile;
            } else {
                // Use default settings.js as it has been modified
                settingsFile = defaultSettings;
            }
        }
    }
}
 
try {
    var settings = require(settingsFile);
    settings.settingsFile = settingsFile;
} catch(err) {
    console.log("Error loading settings file: "+settingsFile)
    if (err.code == 'MODULE_NOT_FOUND') {
        if (err.toString().indexOf(settingsFile) === -1) {
            console.log(err.toString());
        }
    } else {
        console.log(err);
    }
    process.exit();
}
 
Iif (parsedArgs.verbose) {
    settings.verbose = true;
}
 
Iif (settings.https) {
    server = https.createServer(settings.https,function(req,res) {app(req,res);});
} else {
    server = http.createServer(function(req,res) {app(req,res);});
}
server.setMaxListeners(0);
 
function formatRoot(root) {
    Iif (root[0] != "/") {
        root = "/" + root;
    }
    Iif (root.slice(-1) != "/") {
        root = root + "/";
    }
    return root;
}
 
Iif (settings.httpRoot === false) {
    settings.httpAdminRoot = false;
    settings.httpNodeRoot = false;
} else {
    settings.httpRoot = settings.httpRoot||"/";
    settings.disableEditor = settings.disableEditor||false;
}
 
Eif (settings.httpAdminRoot !== false) {
    settings.httpAdminRoot = formatRoot(settings.httpAdminRoot || settings.httpRoot || "/");
    settings.httpAdminAuth = settings.httpAdminAuth || settings.httpAuth;
} else {
    settings.disableEditor = true;
}
 
Eif (settings.httpNodeRoot !== false) {
    settings.httpNodeRoot = formatRoot(settings.httpNodeRoot || settings.httpRoot || "/");
    settings.httpNodeAuth = settings.httpNodeAuth || settings.httpAuth;
}
 
settings.uiPort = parsedArgs.port||settings.uiPort||1880;
settings.uiHost = settings.uiHost||"0.0.0.0";
 
Iif (flowFile) {
    settings.flowFile = flowFile;
}
Iif (parsedArgs.userDir) {
    settings.userDir = parsedArgs.userDir;
}
 
try {
    RED.init(server,settings);
} catch(err) {
    if (err.code == "unsupported_version") {
        console.log("Unsupported version of node.js:",process.version);
        console.log("Node-RED requires node.js v4 or later");
    } else if  (err.code == "not_built") {
        console.log("Node-RED has not been built. See README.md for details");
    } else {
        console.log("Failed to start server:");
        if (err.stack) {
            console.log(err.stack);
        } else {
            console.log(err);
        }
    }
    process.exit(1);
}
 
function basicAuthMiddleware(user,pass) {
    var basicAuth = require('basic-auth');
    var checkPassword;
    if (pass.length == "32") {
        // Assume its a legacy md5 password
        checkPassword = function(p) {
            return crypto.createHash('md5').update(p,'utf8').digest('hex') === pass;
        }
    } else {
        checkPassword = function(p) {
            return bcrypt.compareSync(p,pass);
        }
    }
 
    return function(req,res,next) {
        if (req.method === 'OPTIONS') {
            return next();
        }
        var requestUser = basicAuth(req);
        if (!requestUser || requestUser.name !== user || !checkPassword(requestUser.pass)) {
            res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
            return res.sendStatus(401);
        }
        next();
    }
}
 
Iif (settings.httpAdminRoot !== false && settings.httpAdminAuth) {
    RED.log.warn(RED.log._("server.httpadminauth-deprecated"));
    app.use(settings.httpAdminRoot, basicAuthMiddleware(settings.httpAdminAuth.user,settings.httpAdminAuth.pass));
}
 
Eif (settings.httpAdminRoot !== false) {
    app.use(settings.httpAdminRoot,RED.httpAdmin);
}
Iif (settings.httpNodeRoot !== false && settings.httpNodeAuth) {
    app.use(settings.httpNodeRoot,basicAuthMiddleware(settings.httpNodeAuth.user,settings.httpNodeAuth.pass));
}
Eif (settings.httpNodeRoot !== false) {
    app.use(settings.httpNodeRoot,RED.httpNode);
}
Iif (settings.httpStatic) {
    settings.httpStaticAuth = settings.httpStaticAuth || settings.httpAuth;
    if (settings.httpStaticAuth) {
        app.use("/",basicAuthMiddleware(settings.httpStaticAuth.user,settings.httpStaticAuth.pass));
    }
    app.use("/",express.static(settings.httpStatic));
}
 
function getListenPath() {
    var listenPath = 'http'+(settings.https?'s':'')+'://'+
                    (settings.uiHost == '0.0.0.0'?'127.0.0.1':settings.uiHost)+
                    ':'+settings.uiPort;
    Eif (settings.httpAdminRoot !== false) {
        listenPath += settings.httpAdminRoot;
    } else if (settings.httpStatic) {
        listenPath += "/";
    }
    return listenPath;
}
 
RED.start().then(function() {
    Eif (settings.httpAdminRoot !== false || settings.httpNodeRoot !== false || settings.httpStatic) {
        server.on('error', function(err) {
            Eif (err.errno === "EADDRINUSE") {
                RED.log.error(RED.log._("server.unable-to-listen", {listenpath:getListenPath()}));
                RED.log.error(RED.log._("server.port-in-use"));
            } else {
                RED.log.error(RED.log._("server.uncaught-exception"));
                if (err.stack) {
                    RED.log.error(err.stack);
                } else {
                    RED.log.error(err);
                }
            }
            process.exit(1);
        });
        server.listen(settings.uiPort,settings.uiHost,function() {
            if (settings.httpAdminRoot === false) {
                RED.log.info(RED.log._("server.admin-ui-disabled"));
            }
            process.title = parsedArgs.title || 'node-red';
            RED.log.info(RED.log._("server.now-running", {listenpath:getListenPath()}));
        });
    } else {
        RED.log.info(RED.log._("server.headless-mode"));
    }
}).otherwise(function(err) {
    RED.log.error(RED.log._("server.failed-to-start"));
    if (err.stack) {
        RED.log.error(err.stack);
    } else {
        RED.log.error(err);
    }
});
 
process.on('uncaughtException',function(err) {
    util.log('[red] Uncaught Exception:');
    if (err.stack) {
        util.log(err.stack);
    } else {
        util.log(err);
    }
    process.exit(1);
});
 
process.on('SIGINT', function () {
    RED.stop();
    // TODO: need to allow nodes to close asynchronously before terminating the
    // process - ie, promises
    process.exit();
});
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/settings.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/settings.js

Statements: 100% (1 / 1)      Branches: 50% (1 / 2)      Functions: 100% (0 / 0)      Lines: 100% (1 / 1)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210                                        1                                                                                                                                                                                                                                                                                                                                                                                          
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
// The `https` setting requires the `fs` module. Uncomment the following
// to make it available:
//var fs = require("fs");
 
module.exports = {
    // the tcp port that the Node-RED web server is listening on
    uiPort: process.env.PORT || 1880,
 
    // By default, the Node-RED UI accepts connections on all IPv4 interfaces.
    // The following property can be used to listen on a specific interface. For
    // example, the following would only allow connections from the local machine.
    //uiHost: "127.0.0.1",
 
    // Retry time in milliseconds for MQTT connections
    mqttReconnectTime: 15000,
 
    // Retry time in milliseconds for Serial port connections
    serialReconnectTime: 15000,
 
    // Retry time in milliseconds for TCP socket connections
    //socketReconnectTime: 10000,
 
    // Timeout in milliseconds for TCP server socket connections
    //  defaults to no timeout
    //socketTimeout: 120000,
 
    // Timeout in milliseconds for HTTP request connections
    //  defaults to 120 seconds
    //httpRequestTimeout: 120000,
 
    // The maximum length, in characters, of any message sent to the debug sidebar tab
    debugMaxLength: 1000,
 
    // Colourise the console output of the debug node
    //debugUseColors: true,
 
    // The file containing the flows. If not set, it defaults to flows_<hostname>.json
    //flowFile: 'flows.json',
 
    // To enabled pretty-printing of the flow within the flow file, set the following
    //  property to true:
    //flowFilePretty: true,
 
    // By default, credentials are encrypted in storage using a generated key. To
    // specify your own secret, set the following property.
    // If you want to disable encryption of credentials, set this property to false.
    // Note: once you set this property, do not change it - doing so will prevent
    // node-red from being able to decrypt your existing credentials and they will be
    // lost.
    //credentialSecret: "a-secret-key",
 
    // By default, all user data is stored in the Node-RED install directory. To
    // use a different location, the following property can be used
    //userDir: '/home/nol/.node-red/',
 
    // Node-RED scans the `nodes` directory in the install directory to find nodes.
    // The following property can be used to specify an additional directory to scan.
    //nodesDir: '/home/nol/.node-red/nodes',
 
    // By default, the Node-RED UI is available at http://localhost:1880/
    // The following property can be used to specifiy a different root path.
    // If set to false, this is disabled.
    //httpAdminRoot: '/admin',
 
    // Some nodes, such as HTTP In, can be used to listen for incoming http requests.
    // By default, these are served relative to '/'. The following property
    // can be used to specifiy a different root path. If set to false, this is
    // disabled.
    //httpNodeRoot: '/red-nodes',
 
    // The following property can be used in place of 'httpAdminRoot' and 'httpNodeRoot',
    // to apply the same root to both parts.
    //httpRoot: '/red',
 
    // When httpAdminRoot is used to move the UI to a different root path, the
    // following property can be used to identify a directory of static content
    // that should be served at http://localhost:1880/.
    //httpStatic: '/home/nol/node-red-static/',
 
    // The maximum size of HTTP request that will be accepted by the runtime api.
    // Default: 5mb
    //apiMaxLength: '5mb',
 
    // If you installed the optional node-red-dashboard you can set it's path
    // relative to httpRoot
    //ui: { path: "ui" },
 
    // Securing Node-RED
    // -----------------
    // To password protect the Node-RED editor and admin API, the following
    // property can be used. See http://nodered.org/docs/security.html for details.
    //adminAuth: {
    //    type: "credentials",
    //    users: [{
    //        username: "admin",
    //        password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.",
    //        permissions: "*"
    //    }]
    //},
 
    // To password protect the node-defined HTTP endpoints (httpNodeRoot), or
    // the static content (httpStatic), the following properties can be used.
    // The pass field is a bcrypt hash of the password.
    // See http://nodered.org/docs/security.html#generating-the-password-hash
    //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."},
    //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."},
 
    // The following property can be used to enable HTTPS
    // See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
    // for details on its contents.
    // See the comment at the top of this file on how to load the `fs` module used by
    // this setting.
    //
    //https: {
    //    key: fs.readFileSync('privatekey.pem'),
    //    cert: fs.readFileSync('certificate.pem')
    //},
 
    // The following property can be used to cause insecure HTTP connections to
    // be redirected to HTTPS.
    //requireHttps: true
 
    // The following property can be used to disable the editor. The admin API
    // is not affected by this option. To disable both the editor and the admin
    // API, use either the httpRoot or httpAdminRoot properties
    //disableEditor: false,
 
    // The following property can be used to configure cross-origin resource sharing
    // in the HTTP nodes.
    // See https://github.com/troygoode/node-cors#configuration-options for
    // details on its contents. The following is a basic permissive set of options:
    //httpNodeCors: {
    //    origin: "*",
    //    methods: "GET,PUT,POST,DELETE"
    //},
 
    // If you need to set an http proxy please set an environment variable
    // called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system.
    // For example - http_proxy=http://myproxy.com:8080
    // (Setting it here will have no effect)
    // You may also specify no_proxy (or NO_PROXY) to supply a comma separated
    // list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk
 
    // The following property can be used to add a custom middleware function
    // in front of all http in nodes. This allows custom authentication to be
    // applied to all http in nodes, or any other sort of common request processing.
    //httpNodeMiddleware: function(req,res,next) {
    //    // Handle/reject the request, or pass it on to the http in node by calling next();
    //    // Optionally skip our rawBodyParser by setting this to true;
    //    //req.skipRawBodyParser = true;
    //    next();
    //},
 
    // Anything in this hash is globally available to all functions.
    // It is accessed as context.global.
    // eg:
    //    functionGlobalContext: { os:require('os') }
    // can be accessed in a function block as:
    //    context.global.os
 
    functionGlobalContext: {
        // os:require('os'),
        // octalbonescript:require('octalbonescript'),
        // jfive:require("johnny-five"),
        // j5board:require("johnny-five").Board({repl:false})
    },
 
    // The following property can be used to order the categories in the editor
    // palette. If a node's category is not in the list, the category will get
    // added to the end of the palette.
    // If not set, the following default order is used:
    //paletteCategories: ['subflows', 'input', 'output', 'function', 'social', 'mobile', 'storage', 'analysis', 'advanced'],
 
    // Configure the logging output
    logging: {
        // Only console logging is currently supported
        console: {
            // Level of logging to be recorded. Options are:
            // fatal - only those errors which make the application unusable should be recorded
            // error - record errors which are deemed fatal for a particular request + fatal errors
            // warn - record problems which are non fatal + errors + fatal errors
            // info - record information about the general running of the application + warn + error + fatal errors
            // debug - record information which is more verbose than info + info + warn + error + fatal errors
            // trace - record very detailed logging + debug + info + warn + error + fatal errors
            level: "info",
            // Whether or not to include metric events in the log output
            metrics: false,
            // Whether or not to include audit events in the log output
            audit: false
        }
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/analysis/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/analysis/

Statements: 33.33% (4 / 12)      Branches: 0% (0 / 4)      Functions: 25% (1 / 4)      Lines: 33.33% (4 / 12)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/nodes/core/analysis/
File Statements Branches Functions Lines
72-sentiment.js 33.33% (4 / 12) 0% (0 / 4) 25% (1 / 4) 33.33% (4 / 12)
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/analysis/72-sentiment.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/analysis/72-sentiment.js

Statements: 33.33% (4 / 12)      Branches: 0% (0 / 4)      Functions: 25% (1 / 4)      Lines: 33.33% (4 / 12)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38                                1   1   1                           1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var sentiment = require('sentiment');
 
    function SentimentNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
 
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                sentiment(msg.payload, msg.overrides || null, function (err, result) {
                    msg.sentiment = result;
                    node.send(msg);
                });
            }
            else { node.send(msg); } // If no payload - just pass it on.
        });
    }
    RED.nodes.registerType("sentiment",SentimentNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/

Statements: 11.25% (82 / 729)      Branches: 4.93% (22 / 446)      Functions: 14.43% (14 / 97)      Lines: 11.99% (82 / 684)      Ignored: 3 branches     

All files » node-npmtest-node-red/node_modules/node-red/nodes/core/core/
File Statements Branches Functions Lines
20-inject.js 10% (6 / 60) 0% (0 / 32) 12.5% (1 / 8) 10.91% (6 / 55)
25-catch.js 37.5% (3 / 8) 100% (0 / 0) 33.33% (1 / 3) 37.5% (3 / 8)
25-status.js 37.5% (3 / 8) 100% (0 / 0) 33.33% (1 / 3) 37.5% (3 / 8)
58-debug.js 23.53% (28 / 119) 19.59% (19 / 97) 37.5% (3 / 8) 23.73% (28 / 118)
60-link.js 21.74% (5 / 23) 100% (0 / 0) 14.29% (1 / 7) 21.74% (5 / 23)
75-exec.js 5.56% (6 / 108) 4.11% (3 / 73) 7.69% (1 / 13) 6.82% (6 / 88)
80-function.js 6.19% (7 / 113) 0% (0 / 36) 4% (1 / 25) 6.19% (7 / 113)
80-template.js 19.57% (9 / 46) 0% (0 / 26) 16.67% (1 / 6) 19.57% (9 / 46)
89-delay.js 3.62% (5 / 138) 0% (0 / 76) 7.14% (1 / 14) 3.73% (5 / 134)
89-trigger.js 4.08% (4 / 98) 0% (0 / 106) 16.67% (1 / 6) 4.82% (4 / 83)
90-comment.js 75% (3 / 4) 100% (0 / 0) 50% (1 / 2) 75% (3 / 4)
98-unknown.js 75% (3 / 4) 100% (0 / 0) 50% (1 / 2) 75% (3 / 4)
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/20-inject.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/20-inject.js

Statements: 10% (6 / 60)      Branches: 0% (0 / 32)      Functions: 12.5% (1 / 8)      Lines: 10.91% (6 / 55)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101                                1   1   1                                                                                                     1   1                     1                                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var cron = require("cron");
 
    function InjectNode(n) {
        RED.nodes.createNode(this,n);
        this.topic = n.topic;
        this.payload = n.payload;
        this.payloadType = n.payloadType;
        this.repeat = n.repeat;
        this.crontab = n.crontab;
        this.once = n.once;
        var node = this;
        this.interval_id = null;
        this.cronjob = null;
 
        if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
            this.repeat = this.repeat * 1000;
            if (RED.settings.verbose) { this.log(RED._("inject.repeat",this)); }
            this.interval_id = setInterval( function() {
                node.emit("input",{});
            }, this.repeat );
        } else if (this.crontab) {
            if (RED.settings.verbose) { this.log(RED._("inject.crontab",this)); }
            this.cronjob = new cron.CronJob(this.crontab,
                function() {
                    node.emit("input",{});
                },
                null,true);
        }
 
        if (this.once) {
            setTimeout( function() { node.emit("input",{}); }, 100 );
        }
 
        this.on("input",function(msg) {
            try {
                msg.topic = this.topic;
                if ( (this.payloadType == null && this.payload === "") || this.payloadType === "date") {
                    msg.payload = Date.now();
                } else if (this.payloadType == null) {
                    msg.payload = this.payload;
                } else if (this.payloadType == 'none') {
                    msg.payload = "";
                } else {
                    msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg);
                }
                this.send(msg);
                msg = null;
            } catch(err) {
                this.error(err,msg);
            }
        });
    }
 
    RED.nodes.registerType("inject",InjectNode);
 
    InjectNode.prototype.close = function() {
        if (this.interval_id != null) {
            clearInterval(this.interval_id);
            if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
        } else if (this.cronjob != null) {
            this.cronjob.stop();
            if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
            delete this.cronjob;
        }
    }
 
    RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function(req,res) {
        var node = RED.nodes.getNode(req.params.id);
        if (node != null) {
            try {
                node.receive();
                res.sendStatus(200);
            } catch(err) {
                res.sendStatus(500);
                node.error(RED._("inject.failed",{error:err.toString()}));
            }
        } else {
            res.sendStatus(404);
        }
    });
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/25-catch.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/25-catch.js

Statements: 37.5% (3 / 8)      Branches: 100% (0 / 0)      Functions: 33.33% (1 / 3)      Lines: 37.5% (3 / 8)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32                                1     1                 1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
 
    function CatchNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        this.scope = n.scope;
        this.on("input",function(msg) {
            this.send(msg);
        });
    }
 
    RED.nodes.registerType("catch",CatchNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/25-status.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/25-status.js

Statements: 37.5% (3 / 8)      Branches: 100% (0 / 0)      Functions: 33.33% (1 / 3)      Lines: 37.5% (3 / 8)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32                                1     1                 1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
 
    function StatusNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        this.scope = n.scope;
        this.on("input", function(msg) {
            this.send(msg);
        });
    }
 
    RED.nodes.registerType("status",StatusNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/58-debug.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/58-debug.js

Statements: 23.53% (28 / 119)      Branches: 19.59% (19 / 97)      Functions: 37.5% (3 / 8)      Lines: 23.73% (28 / 118)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209                                1   1 1 1 1 1 1 1   1                                                                                                   1   1 6                       6           6                                                                                                   6     6     6     6       6 6             6     1 1 15 6     1   1                                       1                  
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var util = require("util");
    var events = require("events");
    var path = require("path");
    var safeJSONStringify = require("json-stringify-safe");
    var debuglength = RED.settings.debugMaxLength||1000;
    var useColors = RED.settings.debugUseColors || false;
    util.inspect.styles.boolean = "red";
 
    function DebugNode(n) {
        RED.nodes.createNode(this,n);
        this.name = n.name;
        this.complete = (n.complete||"payload").toString();
 
        if (this.complete === "false") {
            this.complete = "payload";
        }
 
        this.console = n.console;
        this.active = (n.active === null || typeof n.active === "undefined") || n.active;
        var node = this;
 
        this.on("input",function(msg) {
            if (this.complete === "true") {
            // debug complete msg object
                if (this.console === "true") {
                    node.log("\n"+util.inspect(msg, {colors:useColors, depth:10}));
                }
                if (this.active) {
                    sendDebug({id:this.id,name:this.name,topic:msg.topic,msg:msg,_path:msg._path});
                }
            } else {
            // debug user defined msg property
                var property = "payload";
                var output = msg[property];
                if (this.complete !== "false" && typeof this.complete !== "undefined") {
                    property = this.complete;
                    try {
                        output = RED.util.getMessageProperty(msg,this.complete);
                    } catch(err) {
                        output = undefined;
                    }
                }
                if (this.console === "true") {
                    if (typeof output === "string") {
                        node.log((output.indexOf("\n") !== -1 ? "\n" : "") + output);
                    } else if (typeof output === "object") {
                        node.log("\n"+util.inspect(output, {colors:useColors, depth:10}));
                    } else {
                        node.log(util.inspect(output, {colors:useColors}));
                    }
                }
                if (this.active) {
                    sendDebug({id:this.id,z:this.z,name:this.name,topic:msg.topic,property:property,msg:output,_path:msg._path});
                }
            }
        });
    }
 
    RED.nodes.registerType("debug",DebugNode);
 
    function sendDebug(msg) {
        Iif (msg.msg instanceof Error) {
            msg.format = "error";
            var errorMsg = {};
            if (msg.msg.name) {
                errorMsg.name = msg.msg.name;
            }
            if (msg.msg.hasOwnProperty('message')) {
                errorMsg.message = msg.msg.message;
            } else {
                errorMsg.message = msg.msg.toString();
            }
            msg.msg = JSON.stringify(errorMsg);
        } else Iif (msg.msg instanceof Buffer) {
            msg.format = "buffer["+msg.msg.length+"]";
            msg.msg = msg.msg.toString('hex');
            if (msg.msg.length > debuglength) {
                msg.msg = msg.msg.substring(0,debuglength);
            }
        } else Iif (msg.msg && typeof msg.msg === 'object') {
            var seen = [];
            var seenAts = [];
            try {
                msg.format = msg.msg.constructor.name || "Object";
            } catch(err) {
                msg.format = "Object";
            }
            if (/error/i.test(msg.format)) {
                msg.msg = JSON.stringify({
                    name: msg.msg.name,
                    message: msg.msg.message
                });
            } else {
                var isArray = util.isArray(msg.msg);
                if (isArray) {
                    msg.format = "array["+msg.msg.length+"]";
                    if (msg.msg.length > debuglength) {
                        msg.msg = msg.msg.slice(0,debuglength);
                    }
                }
                if (isArray || (msg.format === "Object")) {
                    msg.msg = safeJSONStringify(msg.msg, function(key, value) {
                        if (key === '_req' || key === '_res') {
                            return "[internal]"
                        }
                        if (value instanceof Error) {
                            return value.toString()
                        }
                        if (util.isArray(value) && value.length > debuglength) {
                            value = {
                                __encoded__: true,
                                type: "array",
                                data: value.slice(0,debuglength),
                                length: value.length
                            }
                        }
                        if (typeof value === 'string') {
                            if (value.length > debuglength) {
                                return value.substring(0,debuglength)+"...";
                            }
                        }
                        return value;
                    }," ");
                } else {
                    try { msg.msg = msg.msg.toString(); }
                    catch(e) { msg.msg = "[Type not printable]"; }
                }
            }
            seen = null;
        } else Iif (typeof msg.msg === "boolean") {
            msg.format = "boolean";
            msg.msg = msg.msg.toString();
        } else Iif (typeof msg.msg === "number") {
            msg.format = "number";
            msg.msg = msg.msg.toString();
        } else Iif (msg.msg === 0) {
            msg.format = "number";
            msg.msg = "0";
        } else Iif (msg.msg === null || typeof msg.msg === "undefined") {
            msg.format = (msg.msg === null)?"null":"undefined";
            msg.msg = "(undefined)";
        } else {
            msg.format = "string["+msg.msg.length+"]";
            Iif (msg.msg.length > debuglength) {
                msg.msg = msg.msg.substring(0,debuglength)+"...";
            }
        }
        // if (msg.msg.length > debuglength) {
        //     msg.msg = msg.msg.substr(0,debuglength) +" ....";
        // }
        RED.comms.publish("debug",msg);
    }
 
    DebugNode.logHandler = new events.EventEmitter();
    DebugNode.logHandler.on("log",function(msg) {
        if (msg.level === RED.log.WARN || msg.level === RED.log.ERROR) {
            sendDebug(msg);
        }
    });
    RED.log.addHandler(DebugNode.logHandler);
 
    RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) {
        var node = RED.nodes.getNode(req.params.id);
        var state = req.params.state;
        if (node !== null && typeof node !== "undefined" ) {
            if (state === "enable") {
                node.active = true;
                res.sendStatus(200);
            } else if (state === "disable") {
                node.active = false;
                res.sendStatus(201);
            } else {
                res.sendStatus(404);
            }
        } else {
            res.sendStatus(404);
        }
    });
 
    // As debug/view/debug-utils.js is loaded via <script> tag, it won't get
    // the auth header attached. So do not use RED.auth.needsPermission here.
    RED.httpAdmin.get("/debug/view/*",function(req,res) {
        var options = {
            root: __dirname + '/lib/debug/',
            dotfiles: 'deny'
        };
        res.sendFile(req.params[0], options);
    });
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/60-link.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/60-link.js

Statements: 21.74% (5 / 23)      Branches: 100% (0 / 0)      Functions: 14.29% (1 / 7)      Lines: 21.74% (5 / 23)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52                                1     1                                 1   1                   1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
 
    function LinkInNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        var event = "node:"+n.id;
        var handler = function(msg) {
            msg._event = n.event;
            node.receive(msg);
        }
        RED.events.on(event,handler);
        this.on("input", function(msg) {
            this.send(msg);
        });
        this.on("close",function() {
            RED.events.removeListener(event,handler);
        });
    }
 
    RED.nodes.registerType("link in",LinkInNode);
 
    function LinkOutNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        var event = "node:"+n.id;
        this.on("input", function(msg) {
            msg._event = event;
            RED.events.emit(event,msg)
            this.send(msg);
        });
    }
    RED.nodes.registerType("link out",LinkOutNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/75-exec.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/75-exec.js

Statements: 5.56% (6 / 108)      Branches: 4.11% (3 / 73)      Functions: 7.69% (1 / 13)      Lines: 6.82% (6 / 88)      Ignored: 3 branches     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143                                1   1 1 1   1                                                                                                                                                                                                                                         1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var spawn = require('child_process').spawn;
    var exec = require('child_process').exec;
    var isUtf8 = require('is-utf8');
 
    function ExecNode(n) {
        RED.nodes.createNode(this,n);
        this.cmd = (n.command || "").trim();
        if (n.addpay === undefined) { n.addpay = true; }
        this.addpay = n.addpay;
        this.append = (n.append || "").trim();
        this.useSpawn = n.useSpawn;
        this.timer = Number(n.timer || 0)*1000;
        this.activeProcesses = {};
        var node = this;
 
        var cleanup = function(p) {
            node.activeProcesses[p].kill();
            node.status({fill:"red",shape:"dot",text:"timeout"});
            node.error("Exec node timeout");
        }
 
        this.on("input", function(msg) {
            var child;
            node.status({fill:"blue",shape:"dot",text:" "});
            if (this.useSpawn === true) {
                // make the extra args into an array
                // then prepend with the msg.payload
                var arg = node.cmd;
                if ((node.addpay === true) && msg.hasOwnProperty("payload")) { arg += " "+msg.payload; }
                if (node.append.trim() !== "") { arg += " "+node.append; }
                // slice whole line by spaces (trying to honour quotes);
                arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g);
                var cmd = arg.shift();
                if (/^".*"$/.test(cmd)) { cmd = cmd.slice(1,-1); }
                /* istanbul ignore else  */
                if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
                child = spawn(cmd,arg);
                var unknownCommand = (child.pid === undefined);
                if (node.timer !== 0) {
                    child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
                }
                node.activeProcesses[child.pid] = child;
                child.stdout.on('data', function (data) {
                    if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                        // console.log('[exec] stdout: ' + data,child.pid);
                        if (isUtf8(data)) { msg.payload = data.toString(); }
                        else { msg.payload = data; }
                        node.send([RED.util.cloneMessage(msg),null,null]);
                    }
                });
                child.stderr.on('data', function (data) {
                    if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                        if (isUtf8(data)) { msg.payload = data.toString(); }
                        else { msg.payload = new Buffer(data); }
                        node.send([null,RED.util.cloneMessage(msg),null]);
                    }
                });
                child.on('close', function (code) {
                    if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) {
                        delete node.activeProcesses[child.pid];
                        if (child.tout) { clearTimeout(child.tout); }
                        msg.payload = code;
                        if (code === 0) { node.status({}); }
                        if (code === null) { node.status({fill:"red",shape:"dot",text:"timeout"}); }
                        else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc: "+code}); }
                        else { node.status({fill:"yellow",shape:"dot",text:"rc: "+code}); }
                        node.send([null,null,RED.util.cloneMessage(msg)]);
                    }
                });
                child.on('error', function (code) {
                    if (child.tout) { clearTimeout(child.tout); }
                    delete node.activeProcesses[child.pid];
                    if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                        node.error(code,RED.util.cloneMessage(msg));
                    }
                });
            }
            else {
                var cl = node.cmd;
                if ((node.addpay === true) && msg.hasOwnProperty("payload")) { cl += " "+msg.payload; }
                if (node.append.trim() !== "") { cl += " "+node.append; }
                /* istanbul ignore else  */
                if (RED.settings.verbose) { node.log(cl); }
                child = exec(cl, {encoding: 'binary', maxBuffer:10000000}, function (error, stdout, stderr) {
                    msg.payload = new Buffer(stdout,"binary");
                    if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
                    var msg2 = {payload:stderr};
                    var msg3 = null;
                    //console.log('[exec] stdout: ' + stdout);
                    //console.log('[exec] stderr: ' + stderr);
                    if (error !== null) {
                        msg3 = {payload:error};
                        //console.log('[exec] error: ' + error);
                    }
                    node.status({});
                    node.send([msg,msg2,msg3]);
                    if (child.tout) { clearTimeout(child.tout); }
                    delete node.activeProcesses[child.pid];
                });
                child.on('error',function() {});
                if (node.timer !== 0) {
                    child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
                }
                node.activeProcesses[child.pid] = child;
            }
        });
        this.on('close',function() {
            for (var pid in node.activeProcesses) {
                /* istanbul ignore else  */
                if (node.activeProcesses.hasOwnProperty(pid)) {
                    if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); }
                    // console.log("KILLLING",pid);
                    var process = node.activeProcesses[pid];
                    node.activeProcesses[pid] = null;
                    process.kill();
                }
            }
            node.activeProcesses = {};
            node.status({});
        });
    }
    RED.nodes.registerType("exec",ExecNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/80-function.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/80-function.js

Statements: 6.19% (7 / 113)      Branches: 0% (0 / 36)      Functions: 4% (1 / 25)      Lines: 6.19% (7 / 113)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237                                1   1 1   1                                                         1                                                                                                                                                                                                                                                                                                                                                                           1 1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var util = require("util");
    var vm = require("vm");
 
    function sendResults(node,_msgid,msgs) {
        if (msgs == null) {
            return;
        } else if (!util.isArray(msgs)) {
            msgs = [msgs];
        }
        var msgCount = 0;
        for (var m=0;m<msgs.length;m++) {
            if (msgs[m]) {
                if (util.isArray(msgs[m])) {
                    for (var n=0; n < msgs[m].length; n++) {
                        if (msgs[m][n] !== null && msgs[m][n] !== undefined) {
                            msgs[m][n]._msgid = _msgid;
                            msgCount++;
                        }
                    }
                } else {
                    if (msgs[m] !== null && msgs[m] !== undefined) {
                        msgs[m]._msgid = _msgid;
                        msgCount++;
                    }
                }
            }
        }
        if (msgCount>0) {
            node.send(msgs);
        }
    }
 
    function FunctionNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        this.name = n.name;
        this.func = n.func;
        var functionText = "var results = null;"+
                           "results = (function(msg){ "+
                              "var __msgid__ = msg._msgid;"+
                              "var node = {"+
                                 "log:__node__.log,"+
                                 "error:__node__.error,"+
                                 "warn:__node__.warn,"+
                                 "on:__node__.on,"+
                                 "status:__node__.status,"+
                                 "send:function(msgs){ __node__.send(__msgid__,msgs);}"+
                              "};\n"+
                              this.func+"\n"+
                           "})(msg);";
        this.topic = n.topic;
        this.outstandingTimers = [];
        this.outstandingIntervals = [];
        var sandbox = {
            console:console,
            util:util,
            Buffer:Buffer,
            RED: {
                util: RED.util
            },
            __node__: {
                log: function() {
                    node.log.apply(node, arguments);
                },
                error: function() {
                    node.error.apply(node, arguments);
                },
                warn: function() {
                    node.warn.apply(node, arguments);
                },
                send: function(id, msgs) {
                    sendResults(node, id, msgs);
                },
                on: function() {
                    if (arguments[0] === "input") {
                        throw new Error(RED._("function.error.inputListener"));
                    }
                    node.on.apply(node, arguments);
                },
                status: function() {
                    node.status.apply(node, arguments);
                }
            },
            context: {
                set: function() {
                    node.context().set.apply(node,arguments);
                },
                get: function() {
                    return node.context().get.apply(node,arguments);
                },
                get global() {
                    return node.context().global;
                },
                get flow() {
                    return node.context().flow;
                }
            },
            flow: {
                set: function() {
                    node.context().flow.set.apply(node,arguments);
                },
                get: function() {
                    return node.context().flow.get.apply(node,arguments);
                }
            },
            global: {
                set: function() {
                    node.context().global.set.apply(node,arguments);
                },
                get: function() {
                    return node.context().global.get.apply(node,arguments);
                }
            },
            setTimeout: function () {
                var func = arguments[0];
                var timerId;
                arguments[0] = function() {
                    sandbox.clearTimeout(timerId);
                    try {
                        func.apply(this,arguments);
                    } catch(err) {
                        node.error(err,{});
                    }
                };
                timerId = setTimeout.apply(this,arguments);
                node.outstandingTimers.push(timerId);
                return timerId;
            },
            clearTimeout: function(id) {
                clearTimeout(id);
                var index = node.outstandingTimers.indexOf(id);
                if (index > -1) {
                    node.outstandingTimers.splice(index,1);
                }
            },
            setInterval: function() {
                var func = arguments[0];
                var timerId;
                arguments[0] = function() {
                    try {
                        func.apply(this,arguments);
                    } catch(err) {
                        node.error(err,{});
                    }
                };
                timerId = setInterval.apply(this,arguments);
                node.outstandingIntervals.push(timerId);
                return timerId;
            },
            clearInterval: function(id) {
                clearInterval(id);
                var index = node.outstandingIntervals.indexOf(id);
                if (index > -1) {
                    node.outstandingIntervals.splice(index,1);
                }
            }
        };
        var context = vm.createContext(sandbox);
        try {
            this.script = vm.createScript(functionText);
            this.on("input", function(msg) {
                try {
                    var start = process.hrtime();
                    context.msg = msg;
                    this.script.runInContext(context);
                    sendResults(this,msg._msgid,context.results);
 
                    var duration = process.hrtime(start);
                    var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100;
                    this.metric("duration", msg, converted);
                    if (process.env.NODE_RED_FUNCTION_TIME) {
                        this.status({fill:"yellow",shape:"dot",text:""+converted});
                    }
                } catch(err) {
 
                    var line = 0;
                    var errorMessage;
                    var stack = err.stack.split(/\r?\n/);
                    if (stack.length > 0) {
                        while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
                            line++;
                        }
 
                        if (line < stack.length) {
                            errorMessage = stack[line];
                            var m = /:(\d+):(\d+)$/.exec(stack[line+1]);
                            if (m) {
                                var lineno = Number(m[1])-1;
                                var cha = m[2];
                                errorMessage += " (line "+lineno+", col "+cha+")";
                            }
                        }
                    }
                    if (!errorMessage) {
                        errorMessage = err.toString();
                    }
                    this.error(errorMessage, msg);
                }
            });
            this.on("close", function() {
                while (node.outstandingTimers.length > 0) {
                    clearTimeout(node.outstandingTimers.pop())
                }
                while (node.outstandingIntervals.length > 0) {
                    clearInterval(node.outstandingIntervals.pop())
                }
                this.status({});
            })
        } catch(err) {
            // eg SyntaxError - which v8 doesn't include line number information
            // so we can't do better than this
            this.error(err);
        }
    }
    RED.nodes.registerType("function",FunctionNode);
    RED.library.register("functions");
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/80-template.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/80-template.js

Statements: 19.57% (9 / 46)      Branches: 0% (0 / 26)      Functions: 16.67% (1 / 6)      Lines: 19.57% (9 / 46)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97                                1   1           1         1   1                                                   1       1                                                             1 1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var mustache = require("mustache");
 
    /**
     * Custom Mustache Context capable to resolve message property and node
     * flow and global context
     */
    function NodeContext(msg, nodeContext,parent) {
        this.msgContext = new mustache.Context(msg,parent);
        this.nodeContext = nodeContext;
    }
 
    NodeContext.prototype = new mustache.Context();
 
    NodeContext.prototype.lookup = function (name) {
        // try message first:
        try {
            var value = this.msgContext.lookup(name);
            if (value !== undefined) {
                return value;
            }
 
            // try node context:
            var dot = name.indexOf(".");
            if (dot > 0) {
                var contextName = name.substr(0, dot);
                var variableName = name.substr(dot + 1);
 
                if (contextName === "flow" && this.nodeContext.flow) {
                    return this.nodeContext.flow.get(variableName);
                }
                else if (contextName === "global" && this.nodeContext.global) {
                    return this.nodeContext.global.get(variableName);
                }
            }
        }catch(err) {
            throw err;
        }
    }
 
    NodeContext.prototype.push = function push (view) {
        return new NodeContext(view, this.nodeContext,this.msgContext);
    };
 
    function TemplateNode(n) {
        RED.nodes.createNode(this,n);
        this.name = n.name;
        this.field = n.field || "payload";
        this.template = n.template;
        this.syntax = n.syntax || "mustache";
        this.fieldType = n.fieldType || "msg";
 
        var node = this;
        node.on("input", function(msg) {
            try {
                var value;
                if (node.syntax === "mustache") {
                    value = mustache.render(node.template,new NodeContext(msg, node.context()));
                } else {
                    value = node.template;
                }
                if (node.fieldType === 'msg') {
                    RED.util.setMessageProperty(msg,node.field,value);
                } else if (node.fieldType === 'flow') {
                    node.context().flow.set(node.field,value);
                } else if (node.fieldType === 'global') {
                    node.context().global.set(node.field,value);
                }
                node.send(msg);
            } catch(err) {
                node.error(err.message);
            }
        });
    }
 
    RED.nodes.registerType("template",TemplateNode);
    RED.library.register("templates");
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/89-delay.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/89-delay.js

Statements: 3.62% (5 / 138)      Branches: 0% (0 / 76)      Functions: 7.14% (1 / 14)      Lines: 3.73% (5 / 134)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213                                  1     1 1   1                                                                                                                                                                                                                                                                                                                                                                                   1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
//Simple node to introduce a pause into a flow
module.exports = function(RED) {
    "use strict";
 
    var MILLIS_TO_NANOS = 1000000;
    var SECONDS_TO_NANOS = 1000000000;
 
    function DelayNode(n) {
        RED.nodes.createNode(this,n);
 
        this.pauseType = n.pauseType;
        this.timeoutUnits = n.timeoutUnits;
        this.randomUnits = n.randomUnits;
        this.rateUnits = n.rateUnits;
 
        if (n.timeoutUnits === "milliseconds") {
            this.timeout = n.timeout;
        } else if (n.timeoutUnits === "minutes") {
            this.timeout = n.timeout * (60 * 1000);
        } else if (n.timeoutUnits === "hours") {
            this.timeout = n.timeout * (60 * 60 * 1000);
        } else if (n.timeoutUnits === "days") {
            this.timeout = n.timeout * (24 * 60 * 60 * 1000);
        } else {   // Default to seconds
            this.timeout = n.timeout * 1000;
        }
 
        if (n.rateUnits === "minute") {
            this.rate = (60 * 1000)/n.rate;
        } else if (n.rateUnits === "hour") {
            this.rate = (60 * 60 * 1000)/n.rate;
        } else if (n.rateUnits === "day") {
            this.rate = (24 * 60 * 60 * 1000)/n.rate;
        } else {  // Default to seconds
            this.rate = 1000/n.rate;
        }
 
        this.rate *= (n.nbRateUnits > 0 ? n.nbRateUnits : 1);
 
        if (n.randomUnits === "milliseconds") {
            this.randomFirst = n.randomFirst * 1;
            this.randomLast = n.randomLast * 1;
        } else if (n.randomUnits === "minutes") {
            this.randomFirst = n.randomFirst * (60 * 1000);
            this.randomLast = n.randomLast * (60 * 1000);
        } else if (n.randomUnits === "hours") {
            this.randomFirst = n.randomFirst * (60 * 60 * 1000);
            this.randomLast = n.randomLast * (60 * 60 * 1000);
        } else if (n.randomUnits === "days") {
            this.randomFirst = n.randomFirst * (24 * 60 * 60 * 1000);
            this.randomLast = n.randomLast * (24 * 60 * 60 * 1000);
        } else {  // Default to seconds
            this.randomFirst = n.randomFirst * 1000;
            this.randomLast = n.randomLast * 1000;
        }
 
        this.diff = this.randomLast - this.randomFirst;
        this.name = n.name;
        this.idList = [];
        this.buffer = [];
        this.intervalID = -1;
        this.randomID = -1;
        this.lastSent = null;
        this.drop = n.drop;
        var node = this;
 
        if (node.pauseType === "delay") {
            node.on("input", function(msg) {
                var id;
                id = setTimeout(function() {
                    node.idList.splice(node.idList.indexOf(id),1);
                    if (node.idList.length === 0) { node.status({}); }
                    node.send(msg);
                }, node.timeout);
                node.idList.push(id);
                if ((node.timeout > 1000) && (node.idList.length !== 0)) {
                    node.status({fill:"blue",shape:"dot",text:" "});
                }
            });
 
            node.on("close", function() {
                for (var i=0; i<node.idList.length; i++ ) {
                    clearTimeout(node.idList[i]);
                }
                node.idList = [];
                node.status({});
            });
 
        } else if (node.pauseType === "rate") {
            var olddepth = 0;
            node.on("input", function(msg) {
                if (!node.drop) {
                    if ( node.intervalID !== -1) {
                        node.buffer.push(msg);
                        if (node.buffer.length > 0) {
                            node.status({text:node.buffer.length});
                        }
                        if ((node.buffer.length > 1000) && (olddepth < 1000)) {
                            olddepth = 1000;
                            node.warn(node.name + " " + RED._("delay.error.buffer"));
                        }
                        if ((node.buffer.length > 10000) && (olddepth < 10000)) {
                            olddepth = 10000;
                            node.warn(node.name + " " + RED._("delay.error.buffer1"));
                        }
                    } else {
                        node.send(msg);
                        node.intervalID = setInterval(function() {
                            if (node.buffer.length === 0) {
                                clearInterval(node.intervalID);
                                node.intervalID = -1;
                                node.status({});
                            }
                            if (node.buffer.length > 0) {
                                node.send(node.buffer.shift());
                                if (node.buffer.length < 1000) { olddepth = 0; }
                                node.status({text:node.buffer.length});
                            }
                        },node.rate);
                    }
                } else {
                    var timeSinceLast;
                    if (node.lastSent) {
                        timeSinceLast = process.hrtime(node.lastSent);
                    }
                    if (!node.lastSent) { // ensuring that we always send the first message
                        node.lastSent = process.hrtime();
                        node.send(msg);
                    } else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) {
                        node.lastSent = process.hrtime();
                        node.send(msg);
                    }
                }
            });
 
            node.on("close", function() {
                clearInterval(node.intervalID);
                node.buffer = [];
                node.status({});
            });
 
        } else if ((node.pauseType === "queue") || (node.pauseType === "timed")) {
            node.intervalID = setInterval(function() {
                if (node.pauseType === "queue") {
                    if (node.buffer.length > 0) {
                        node.send(node.buffer.shift()); // send the first on the queue
                    }
                }
                else {
                    while (node.buffer.length > 0) {    // send the whole queue
                        node.send(node.buffer.shift());
                    }
                }
                node.status({text:node.buffer.length});
            },node.rate);
 
            node.on("input", function(msg) {
                if (!msg.hasOwnProperty("topic")) { msg.topic = "_none_"; }
                var hit = false;
                for (var b in node.buffer) { // check if already in queue
                    if (msg.topic === node.buffer[b].topic) {
                        node.buffer[b] = msg; // if so - replace existing entry
                        hit = true;
                    }
                }
                if (!hit) { node.buffer.push(msg); } // if not add to end of queue
                node.status({text:node.buffer.length});
            });
 
            node.on("close", function() {
                clearInterval(node.intervalID);
                node.buffer = [];
                node.status({});
            });
 
        } else if (node.pauseType === "random") {
            node.on("input", function(msg) {
                var wait = node.randomFirst + (node.diff * Math.random());
                var id = setTimeout(function() {
                    node.idList.splice(node.idList.indexOf(id),1);
                    node.send(msg);
                }, wait);
                node.idList.push(id);
            });
            
            node.on("close", function() {
                for (var i=0; i<node.idList.length; i++ ) {
                    clearTimeout(node.idList[i]);
                }
                node.idList = [];
            });
        }
    }
    RED.nodes.registerType("delay",DelayNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/89-trigger.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/89-trigger.js

Statements: 4.08% (4 / 98)      Branches: 0% (0 / 106)      Functions: 16.67% (1 / 6)      Lines: 4.82% (4 / 83)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140                                1   1 1                                                                                                                                                                                                                                         1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var mustache = require("mustache");
    function TriggerNode(n) {
        RED.nodes.createNode(this,n);
        this.op1 = n.op1 || "1";
        this.op2 = n.op2 || "0";
        this.op1type = n.op1type || "str";
        this.op2type = n.op2type || "str";
 
        if (this.op1type === 'val') {
            if (this.op1 === 'true' || this.op1 === 'false') {
                this.op1type = 'bool'
            } else if (this.op1 === 'null') {
                this.op1type = 'null';
                this.op1 = null;
            } else {
                this.op1type = 'str';
            }
        }
        if (this.op2type === 'val') {
            if (this.op2 === 'true' || this.op2 === 'false') {
                this.op2type = 'bool'
            } else if (this.op2 === 'null') {
                this.op2type = 'null';
                this.op2 = null;
            } else {
                this.op2type = 'str';
            }
        }
        this.extend = n.extend || "false";
        this.units = n.units || "ms";
        this.reset = n.reset || '';
        this.duration = n.duration || 250;
        if (this.duration <= 0) { this.duration = 0; }
        else {
            if (this.units == "s") { this.duration = this.duration * 1000; }
            if (this.units == "min") { this.duration = this.duration * 1000 * 60; }
            if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; }
        }
        this.op1Templated = (this.op1type === 'str' && this.op1.indexOf("{{") != -1);
        this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1);
        if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); }
        if ((this.op2type === "num") && (!isNaN(this.op2))) { this.op2 = Number(this.op2); }
        if (this.op1 == "null") { this.op1 = null; }
        if (this.op2 == "null") { this.op2 = null; }
        //try { this.op1 = JSON.parse(this.op1); }
        //catch(e) { this.op1 = this.op1; }
        //try { this.op2 = JSON.parse(this.op2); }
        //catch(e) { this.op2 = this.op2; }
 
        var node = this;
        var tout = null;
        var m2;
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("reset") || ((node.reset !== '')&&(msg.payload == node.reset)) ) {
                clearTimeout(tout);
                tout = null;
                node.status({});
            }
            else {
                if ((!tout) && (tout !== 0)) {
                    if (node.op2type === "pay" || node.op2type === "payl") { m2 = msg.payload; }
                    else if (node.op2Templated) { m2 = mustache.render(node.op2,msg); }
                    else if (node.op2type !== "nul") {
                        m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
                    }
 
                    if (node.op1type === "pay") { }
                    else if (node.op1Templated) { msg.payload = mustache.render(node.op1,msg); }
                    else if (node.op1type !== "nul") {
                        msg.payload = RED.util.evaluateNodeProperty(node.op1,node.op1type,node,msg);
                    }
 
                    if (node.op1type !== "nul") { node.send(msg); }
 
                    if (node.duration === 0) { tout = 0; }
                    else {
                        tout = setTimeout(function() {
                            if (node.op2type !== "nul") {
                                var msg2 = RED.util.cloneMessage(msg);
                                if (node.op2type === "flow" || node.op2type === "global") {
                                    m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
                                }
                                msg2.payload = m2;
                                node.send(msg2);
                            }
                            tout = null;
                            node.status({});
                        },node.duration);
                    }
                    node.status({fill:"blue",shape:"dot",text:" "});
                }
                else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) {
                    clearTimeout(tout);
                    if (node.op2type === "payl") { m2 = msg.payload; }
                    tout = setTimeout(function() {
                        if (node.op2type !== "nul") {
                            var msg2 = RED.util.cloneMessage(msg);
                            if (node.op2type === "flow" || node.op2type === "global") {
                                m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
                            }
                            msg2.payload = m2;
                            node.send(msg2);
                        }
                        tout = null;
                        node.status({});
                    },node.duration);
                } else {
                    if (node.op2type === "payl") { m2 = msg.payload; }
                }
            }
        });
        this.on("close", function() {
            if (tout) {
                clearTimeout(tout);
            }
            node.status({});
        });
    }
    RED.nodes.registerType("trigger",TriggerNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/90-comment.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/90-comment.js

Statements: 75% (3 / 4)      Branches: 100% (0 / 0)      Functions: 50% (1 / 2)      Lines: 75% (3 / 4)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25                                1   1     1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    function CommentNode(n) {
        RED.nodes.createNode(this,n);
    }
    RED.nodes.registerType("comment",CommentNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/98-unknown.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/core/98-unknown.js

Statements: 75% (3 / 4)      Branches: 100% (0 / 0)      Functions: 50% (1 / 2)      Lines: 75% (3 / 4)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25                                1   1     1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    function UnknownNode(n) {
        RED.nodes.createNode(this,n);
    }
    RED.nodes.registerType("unknown",UnknownNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/hardware/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/hardware/

Statements: 6.44% (15 / 233)      Branches: 0.85% (1 / 117)      Functions: 3.45% (1 / 29)      Lines: 6.8% (14 / 206)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/nodes/core/hardware/
File Statements Branches Functions Lines
36-rpi-gpio.js 6.44% (15 / 233) 0.85% (1 / 117) 3.45% (1 / 29) 6.8% (14 / 206)
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/hardware/36-rpi-gpio.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/hardware/36-rpi-gpio.js

Statements: 6.44% (15 / 233)      Branches: 0.85% (1 / 117)      Functions: 3.45% (1 / 29)      Lines: 6.8% (14 / 206)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342                                1   1 1 1   1   1 1 1   1                                                     1                                                                                                                                                   1                               1                                                                                                                                                       1                                                                                             1                                                                                                                                                  
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var exec = require('child_process').exec;
    var spawn = require('child_process').spawn;
    var fs = require('fs');
 
    var gpioCommand = __dirname+'/nrgpio';
 
    try {
        var cpuinfo = fs.readFileSync("/proc/cpuinfo").toString();
        Eif (cpuinfo.indexOf(": BCM") === -1) { throw "Info : "+RED._("rpi-gpio.errors.ignorenode"); }
    } catch(err) {
        throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
    }
 
    try {
        fs.statSync("/usr/share/doc/python-rpi.gpio"); // test on Raspbian
        // /usr/lib/python2.7/dist-packages/RPi/GPIO
    } catch(err) {
        try {
            fs.statSync("/usr/lib/python2.7/site-packages/RPi/GPIO"); // test on Arch
        }
        catch(err) {
            RED.log.warn(RED._("rpi-gpio.errors.libnotfound"));
            throw "Warning : "+RED._("rpi-gpio.errors.libnotfound");
        }
    }
 
    if ( !(1 & parseInt((fs.statSync(gpioCommand).mode & parseInt("777", 8)).toString(8)[0]) )) {
        RED.log.error(RED._("rpi-gpio.errors.needtobeexecutable",{command:gpioCommand}));
        throw "Error : "+RED._("rpi-gpio.errors.mustbeexecutable");
    }
 
    // the magic to make python print stuff immediately
    process.env.PYTHONUNBUFFERED = 1;
 
    var pinsInUse = {};
    var pinTypes = {"out":RED._("rpi-gpio.types.digout"), "tri":RED._("rpi-gpio.types.input"), "up":RED._("rpi-gpio.types.pullup"), "down":RED._("rpi-gpio.types.pulldown"), "pwm":RED._("rpi-gpio.types.pwmout")};
 
    function GPIOInNode(n) {
        RED.nodes.createNode(this,n);
        this.buttonState = -1;
        this.pin = n.pin;
        this.intype = n.intype;
        this.read = n.read || false;
        this.debounce = Number(n.debounce || 25);
        if (this.read) { this.buttonState = -2; }
        var node = this;
        if (!pinsInUse.hasOwnProperty(this.pin)) {
            pinsInUse[this.pin] = this.intype;
        }
        else {
            if ((pinsInUse[this.pin] !== this.intype)||(pinsInUse[this.pin] === "pwm")) {
                node.warn(RED._("rpi-gpio.errors.alreadyset",{pin:this.pin,type:pinTypes[pinsInUse[this.pin]]}));
            }
        }
 
        if (node.pin !== undefined) {
            node.child = spawn(gpioCommand, ["in",node.pin,node.intype,node.debounce]);
            node.running = true;
            node.status({fill:"green",shape:"dot",text:"common.status.ok"});
 
            node.child.stdout.on('data', function (data) {
                var d = data.toString().trim().split("\n");
                for (var i = 0; i < d.length; i++) {
                    if (node.running && node.buttonState !== -1 && !isNaN(Number(d[i]))) {
                        node.send({ topic:"pi/"+node.pin, payload:Number(d[i]) });
                    }
                    node.buttonState = d[i];
                    node.status({fill:"green",shape:"dot",text:d[i]});
                    if (RED.settings.verbose) { node.log("out: "+d[i]+" :"); }
                }
            });
 
            node.child.stderr.on('data', function (data) {
                if (RED.settings.verbose) { node.log("err: "+data+" :"); }
            });
 
            node.child.on('close', function (code) {
                node.running = false;
                node.child = null;
                if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
                if (node.done) {
                    node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
                    node.done();
                }
                else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
            });
 
            node.child.on('error', function (err) {
                if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
                else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
                else { node.error(RED._("rpi-gpio.errors.error",{error:err.errno})) }
            });
 
        }
        else {
            node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin);
        }
 
        node.on("close", function(done) {
            node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
            delete pinsInUse[node.pin];
            if (node.child != null) {
                node.done = done;
                node.child.stdin.write("close "+node.pin);
                node.child.kill('SIGKILL');
            }
            else { done(); }
        });
    }
    RED.nodes.registerType("rpi-gpio in",GPIOInNode);
 
    function GPIOOutNode(n) {
        RED.nodes.createNode(this,n);
        this.pin = n.pin;
        this.set = n.set || false;
        this.level = n.level || 0;
        this.out = n.out || "out";
        var node = this;
        if (!pinsInUse.hasOwnProperty(this.pin)) {
            pinsInUse[this.pin] = this.out;
        }
        else {
            if ((pinsInUse[this.pin] !== this.out)||(pinsInUse[this.pin] === "pwm")) {
                node.warn(RED._("rpi-gpio.errors.alreadyset",{pin:this.pin,type:pinTypes[pinsInUse[this.pin]]}));
            }
        }
 
        function inputlistener(msg) {
            if (msg.payload === "true") { msg.payload = true; }
            if (msg.payload === "false") { msg.payload = false; }
            var out = Number(msg.payload);
            var limit = 1;
            if (node.out === "pwm") { limit = 100; }
            if ((out >= 0) && (out <= limit)) {
                if (RED.settings.verbose) { node.log("out: "+msg.payload); }
                if (node.child !== null) {
                    node.child.stdin.write(msg.payload+"\n");
                    node.status({fill:"green",shape:"dot",text:msg.payload.toString()});
                }
                else {
                    node.error(RED._("rpi-gpio.errors.pythoncommandnotfound"),msg);
                    node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.not-running"});
                }
            }
            else { node.warn(RED._("rpi-gpio.errors.invalidinput")+": "+out); }
        }
 
        if (node.pin !== undefined) {
            if (node.set && (node.out === "out")) {
                node.child = spawn(gpioCommand, [node.out,node.pin,node.level]);
                node.status({fill:"green",shape:"dot",text:node.level});
            } else {
                node.child = spawn(gpioCommand, [node.out,node.pin]);
                node.status({fill:"green",shape:"dot",text:"common.status.ok"});
            }
            node.running = true;
 
            node.on("input", inputlistener);
 
            node.child.stdout.on('data', function (data) {
                if (RED.settings.verbose) { node.log("out: "+data+" :"); }
            });
 
            node.child.stderr.on('data', function (data) {
                if (RED.settings.verbose) { node.log("err: "+data+" :"); }
            });
 
            node.child.on('close', function (code) {
                node.child = null;
                node.running = false;
                if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
                if (node.done) {
                    node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
                    node.done();
                }
                else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
            });
 
            node.child.on('error', function (err) {
                if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
                else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
                else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
            });
 
        }
        else {
            node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin);
        }
 
        node.on("close", function(done) {
            node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
            delete pinsInUse[node.pin];
            if (node.child != null) {
                node.done = done;
                node.child.stdin.write("close "+node.pin);
                node.child.kill('SIGKILL');
            }
            else { done(); }
        });
 
    }
    RED.nodes.registerType("rpi-gpio out",GPIOOutNode);
 
    function PiMouseNode(n) {
        RED.nodes.createNode(this,n);
        this.butt = n.butt || 7;
        var node = this;
 
        node.child = spawn(gpioCommand+".py", ["mouse",node.butt]);
        node.status({fill:"green",shape:"dot",text:"common.status.ok"});
 
        node.child.stdout.on('data', function (data) {
            data = Number(data);
            if (data === 1) { node.send({ topic:"pi/mouse", button:data, payload:1 }); }
            else { node.send({ topic:"pi/mouse", button:data, payload:0 }); }
        });
 
        node.child.stderr.on('data', function (data) {
            if (RED.settings.verbose) { node.log("err: "+data+" :"); }
        });
 
        node.child.on('close', function (code) {
            node.child = null;
            node.running = false;
            if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
            if (node.done) {
                node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
                node.done();
            }
            else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
        });
 
        node.child.on('error', function (err) {
            if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
            else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
            else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
        });
 
        node.on("close", function(done) {
            node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
            if (node.child != null) {
                node.done = done;
                node.child.kill('SIGINT');
                node.child = null;
            }
            else { done(); }
        });
    }
    RED.nodes.registerType("rpi-mouse",PiMouseNode);
 
    function PiKeyboardNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
 
        node.child = spawn(gpioCommand+".py", ["kbd","0"]);
        node.status({fill:"green",shape:"dot",text:"common.status.ok"});
 
        node.child.stdout.on('data', function (data) {
            var b = data.toString().trim().split(",");
            var act = "up";
            if (b[1] === "1") { act = "down"; }
            if (b[1] === "2") { act = "repeat"; }
            node.send({ topic:"pi/key", payload:Number(b[0]), action:act });
        });
 
        node.child.stderr.on('data', function (data) {
            if (RED.settings.verbose) { node.log("err: "+data+" :"); }
        });
 
        node.child.on('close', function (code) {
            node.running = false;
            node.child = null;
            if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); }
            if (node.done) {
                node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"});
                node.done();
            }
            else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); }
        });
 
        node.child.on('error', function (err) {
            if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); }
            else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); }
            else { node.error(RED._("rpi-gpio.errors.error")+': ' + err.errno); }
        });
 
        node.on("close", function(done) {
            node.status({});
            if (node.child != null) {
                node.done = done;
                node.child.kill('SIGINT');
                node.child = null;
            }
            else { done(); }
        });
    }
    RED.nodes.registerType("rpi-keyboard",PiKeyboardNode);
 
    var pitype = { type:"" };
    exec(gpioCommand+" info", function(err,stdout,stderr) {
        if (err) {
            RED.log.info(RED._("rpi-gpio.errors.version"));
        }
        else {
            try {
                var info = JSON.parse( stdout.trim().replace(/\'/g,"\"") );
                pitype.type = info["TYPE"];
            }
            catch(e) {
                RED.log.info(RED._("rpi-gpio.errors.sawpitype"),stdout.trim());
            }
        }
    });
 
    RED.httpAdmin.get('/rpi-gpio/:id', RED.auth.needsPermission('rpi-gpio.read'), function(req,res) {
        res.json(pitype);
    });
 
    RED.httpAdmin.get('/rpi-pins/:id', RED.auth.needsPermission('rpi-gpio.read'), function(req,res) {
        res.json(pinsInUse);
    });
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/

Statements: 6.6% (86 / 1304)      Branches: 0.62% (5 / 810)      Functions: 5.3% (8 / 151)      Lines: 6.91% (86 / 1245)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/nodes/core/io/
File Statements Branches Functions Lines
05-tls.js 14.71% (5 / 34) 0% (0 / 16) 33.33% (1 / 3) 14.71% (5 / 34)
10-mqtt.js 4.7% (11 / 234) 0% (0 / 184) 4.35% (1 / 23) 4.74% (11 / 232)
21-httpin.js 12.58% (20 / 159) 1.04% (1 / 96) 4.76% (1 / 21) 13.07% (20 / 153)
21-httprequest.js 5.48% (8 / 146) 0% (0 / 121) 10% (1 / 10) 5.8% (8 / 138)
22-websocket.js 11.11% (17 / 153) 0% (0 / 65) 3.33% (1 / 30) 12.14% (17 / 140)
23-watch.js 13.95% (6 / 43) 0% (0 / 20) 20% (1 / 5) 17.14% (6 / 35)
31-tcpin.js 2.7% (11 / 408) 1.72% (4 / 233) 2.17% (1 / 46) 2.81% (11 / 391)
32-udp.js 6.3% (8 / 127) 0% (0 / 75) 7.69% (1 / 13) 6.56% (8 / 122)
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/05-tls.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/05-tls.js

Statements: 14.71% (5 / 34)      Branches: 0% (0 / 16)      Functions: 33.33% (1 / 3)      Lines: 14.71% (5 / 34)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71                                1 1     1                                                           1   1                                    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var fs = require('fs');
module.exports = function(RED) {
    "use strict";
 
    function TLSConfig(n) {
        RED.nodes.createNode(this,n);
        this.valid = true;
        var certPath = n.cert.trim();
        var keyPath = n.key.trim();
        var caPath = n.ca.trim();
 
        if ( (certPath.length > 0) !== (keyPath.length > 0)) {
            this.valid = false;
            this.error(RED._("tls.error.missing-file"));
            return;
        }
        this.verifyservercert = n.verifyservercert;
 
        try {
            if (certPath) {
                this.cert = fs.readFileSync(certPath);
            }
            if (keyPath) {
                this.key = fs.readFileSync(keyPath);
            }
            if (caPath) {
                this.ca = fs.readFileSync(caPath);
            }
        } catch(err) {
            this.valid = false;
            this.error(err.toString());
            return;
        }
    }
    RED.nodes.registerType("tls-config",TLSConfig);
 
    TLSConfig.prototype.addTLSOptions = function(opts) {
        if (this.valid) {
            if (this.key) {
                opts.key = this.key;
            }
            if (this.cert) {
                opts.cert = this.cert;
            }
            if (this.ca) {
                opts.ca = this.ca;
            }
            opts.rejectUnauthorized = this.verifyservercert;
        }
        return opts;
    }
 
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/10-mqtt.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/10-mqtt.js

Statements: 4.7% (11 / 234)      Branches: 0% (0 / 184)      Functions: 4.35% (1 / 23)      Lines: 4.74% (11 / 232)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407                                1   1 1 1   1               1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               1             1                                                                                   1   1                                                                                   1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var mqtt = require("mqtt");
    var util = require("util");
    var isUtf8 = require('is-utf8');
 
    function matchTopic(ts,t) {
        if (ts == "#") {
            return true;
        }
        var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
        return re.test(t);
    }
 
    function MQTTBrokerNode(n) {
        RED.nodes.createNode(this,n);
 
        // Configuration options passed by Node Red
        this.broker = n.broker;
        this.port = n.port;
        this.clientid = n.clientid;
        this.usetls = n.usetls;
        this.verifyservercert = n.verifyservercert;
        this.compatmode = n.compatmode;
        this.keepalive = n.keepalive;
        this.cleansession = n.cleansession;
 
        // Config node state
        this.brokerurl = "";
        this.connected = false;
        this.connecting = false;
        this.closing = false;
        this.options = {};
        this.queue = [];
        this.subscriptions = {};
 
        if (n.birthTopic) {
            this.birthMessage = {
                topic: n.birthTopic,
                payload: n.birthPayload || "",
                qos: Number(n.birthQos||0),
                retain: n.birthRetain=="true"|| n.birthRetain===true
            };
        }
 
        if (this.credentials) {
            this.username = this.credentials.user;
            this.password = this.credentials.password;
        }
 
        // If the config node is missing certain options (it was probably deployed prior to an update to the node code),
        // select/generate sensible options for the new fields
        if (typeof this.usetls === 'undefined'){
            this.usetls = false;
        }
        if (typeof this.compatmode === 'undefined'){
            this.compatmode = true;
        }
        if (typeof this.verifyservercert === 'undefined'){
            this.verifyservercert = false;
        }
        if (typeof this.keepalive === 'undefined'){
            this.keepalive = 60;
        } else if (typeof this.keepalive === 'string') {
            this.keepalive = Number(this.keepalive);
        }
        if (typeof this.cleansession === 'undefined') {
            this.cleansession = true;
        }
 
        // Create the URL to pass in to the MQTT.js library
        if (this.brokerurl === "") {
            if (this.usetls) {
                this.brokerurl="mqtts://";
            } else {
                this.brokerurl="mqtt://";
            }
            if (this.broker !== "") {
                this.brokerurl = this.brokerurl+this.broker+":"+this.port;
            } else {
                this.brokerurl = this.brokerurl+"localhost:1883";
            }
        }
 
        if (!this.cleansession && !this.clientid) {
            this.cleansession = true;
            this.warn(RED._("mqtt.errors.nonclean-missingclientid"));
        }
 
        // Build options for passing to the MQTT.js API
        this.options.clientId = this.clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16);
        this.options.username = this.username;
        this.options.password = this.password;
        this.options.keepalive = this.keepalive;
        this.options.clean = this.cleansession;
        this.options.reconnectPeriod = RED.settings.mqttReconnectTime||5000;
        if (this.compatmode == "true" || this.compatmode === true){
            this.options.protocolId = 'MQIsdp';
            this.options.protocolVersion = 3;
        }
        if (this.usetls && n.tls) {
            var tlsNode = RED.nodes.getNode(n.tls);
            if (tlsNode) {
                tlsNode.addTLSOptions(this.options);
            }
        }
        // If there's no rejectUnauthorized already, then this could be an
        // old config where this option was provided on the broker node and
        // not the tls node
        if (typeof this.options.rejectUnauthorized === 'undefined') {
            this.options.rejectUnauthorized = (this.verifyservercert == "true" || this.verifyservercert === true);
        }
 
        if (n.willTopic) {
            this.options.will = {
                topic: n.willTopic,
                payload: n.willPayload || "",
                qos: Number(n.willQos||0),
                retain: n.willRetain=="true"|| n.willRetain===true
            };
        }
 
        // Define functions called by MQTT in and out nodes
        var node = this;
        this.users = {};
 
        this.register = function(mqttNode){
            node.users[mqttNode.id] = mqttNode;
            if (Object.keys(node.users).length === 1) {
                node.connect();
            }
        };
 
        this.deregister = function(mqttNode,done){
            delete node.users[mqttNode.id];
            if (node.closing) {
                return done();
            }
            if (Object.keys(node.users).length === 0) {
                if (node.client && node.client.connected) {
                    return node.client.end(done);
                } else {
                    node.client.end();
                    return done();
                }
            }
            done();
        };
 
        this.connect = function () {
            if (!node.connected && !node.connecting) {
                node.connecting = true;
                node.client = mqtt.connect(node.brokerurl ,node.options);
                node.client.setMaxListeners(0);
                // Register successful connect or reconnect handler
                node.client.on('connect', function () {
                    node.connecting = false;
                    node.connected = true;
                    node.log(RED._("mqtt.state.connected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
                    for (var id in node.users) {
                        if (node.users.hasOwnProperty(id)) {
                            node.users[id].status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
                        }
                    }
                    // Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection
                    node.client.removeAllListeners('message');
 
                    // Re-subscribe to stored topics
                    for (var s in node.subscriptions) {
                        if (node.subscriptions.hasOwnProperty(s)) {
                            var topic = s;
                            var qos = 0;
                            for (var r in node.subscriptions[s]) {
                                if (node.subscriptions[s].hasOwnProperty(r)) {
                                    qos = Math.max(qos,node.subscriptions[s][r].qos);
                                    node.client.on('message',node.subscriptions[s][r].handler);
                                }
                            }
                            var options = {qos: qos};
                            node.client.subscribe(topic, options);
                        }
                    }
 
                    // Send any birth message
                    if (node.birthMessage) {
                        node.publish(node.birthMessage);
                    }
                });
                node.client.on("reconnect", function() {
                    for (var id in node.users) {
                        if (node.users.hasOwnProperty(id)) {
                            node.users[id].status({fill:"yellow",shape:"ring",text:"node-red:common.status.connecting"});
                        }
                    }
                })
                // Register disconnect handlers
                node.client.on('close', function () {
                    if (node.connected) {
                        node.connected = false;
                        node.log(RED._("mqtt.state.disconnected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
                        for (var id in node.users) {
                            if (node.users.hasOwnProperty(id)) {
                                node.users[id].status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
                            }
                        }
                    } else if (node.connecting) {
                        node.log(RED._("mqtt.state.connect-failed",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
                    }
                });
 
                // Register connect error handler
                node.client.on('error', function (error) {
                    if (node.connecting) {
                        node.client.end();
                        node.connecting = false;
                    }
                });
            }
        };
 
        this.subscribe = function (topic,qos,callback,ref) {
            ref = ref||0;
            node.subscriptions[topic] = node.subscriptions[topic]||{};
            var sub = {
                topic:topic,
                qos:qos,
                handler:function(mtopic,mpayload, mpacket) {
                    if (matchTopic(topic,mtopic)) {
                        callback(mtopic,mpayload, mpacket);
                    }
                },
                ref: ref
            };
            node.subscriptions[topic][ref] = sub;
            if (node.connected) {
                node.client.on('message',sub.handler);
                var options = {};
                options.qos = qos;
                node.client.subscribe(topic, options);
            }
        };
 
        this.unsubscribe = function (topic, ref) {
            ref = ref||0;
            var sub = node.subscriptions[topic];
            if (sub) {
                if (sub[ref]) {
                    node.client.removeListener('message',sub[ref].handler);
                    delete sub[ref];
                }
                if (Object.keys(sub).length === 0) {
                    delete node.subscriptions[topic];
                    if (node.connected){
                        node.client.unsubscribe(topic);
                    }
                }
            }
        };
 
        this.publish = function (msg) {
            if (node.connected) {
                if (!Buffer.isBuffer(msg.payload)) {
                    if (typeof msg.payload === "object") {
                        msg.payload = JSON.stringify(msg.payload);
                    } else if (typeof msg.payload !== "string") {
                        msg.payload = "" + msg.payload;
                    }
                }
 
                var options = {
                    qos: msg.qos || 0,
                    retain: msg.retain || false
                };
                node.client.publish(msg.topic, msg.payload, options, function (err){return});
            }
        };
 
        this.on('close', function(done) {
            this.closing = true;
            if (this.connected) {
                this.client.once('close', function() {
                    done();
                });
                this.client.end();
            } else if (this.connecting) {
                node.client.end();
                done();
            } else {
                done();
            }
        });
 
    }
 
    RED.nodes.registerType("mqtt-broker",MQTTBrokerNode,{
        credentials: {
            user: {type:"text"},
            password: {type: "password"}
        }
    });
 
    function MQTTInNode(n) {
        RED.nodes.createNode(this,n);
        this.topic = n.topic;
        this.qos = parseInt(n.qos);
        if (isNaN(this.qos) || this.qos < 0 || this.qos > 2) {
            this.qos = 2;
        }
        this.broker = n.broker;
        this.brokerConn = RED.nodes.getNode(this.broker);
        if (!/^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(this.topic)) {
            return this.warn(RED._("mqtt.errors.invalid-topic"));
        }
        var node = this;
        if (this.brokerConn) {
            this.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
            if (this.topic) {
                node.brokerConn.register(this);
                this.brokerConn.subscribe(this.topic,this.qos,function(topic,payload,packet) {
                    if (isUtf8(payload)) { payload = payload.toString(); }
                    var msg = {topic:topic,payload:payload, qos: packet.qos, retain: packet.retain};
                    if ((node.brokerConn.broker === "localhost")||(node.brokerConn.broker === "127.0.0.1")) {
                        msg._topic = topic;
                    }
                    node.send(msg);
                }, this.id);
                if (this.brokerConn.connected) {
                    node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
                }
            }
            else {
                this.error(RED._("mqtt.errors.not-defined"));
            }
            this.on('close', function(done) {
                if (node.brokerConn) {
                    node.brokerConn.unsubscribe(node.topic,node.id);
                    node.brokerConn.deregister(node,done);
                }
            });
        } else {
            this.error(RED._("mqtt.errors.missing-config"));
        }
    }
    RED.nodes.registerType("mqtt in",MQTTInNode);
 
    function MQTTOutNode(n) {
        RED.nodes.createNode(this,n);
        this.topic = n.topic;
        this.qos = n.qos || null;
        this.retain = n.retain;
        this.broker = n.broker;
        this.brokerConn = RED.nodes.getNode(this.broker);
        var node = this;
 
        if (this.brokerConn) {
            this.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
            this.on("input",function(msg) {
                if (msg.qos) {
                    msg.qos = parseInt(msg.qos);
                    if ((msg.qos !== 0) && (msg.qos !== 1) && (msg.qos !== 2)) {
                        msg.qos = null;
                    }
                }
                msg.qos = Number(node.qos || msg.qos || 0);
                msg.retain = node.retain || msg.retain || false;
                msg.retain = ((msg.retain === true) || (msg.retain === "true")) || false;
                if (node.topic) {
                    msg.topic = node.topic;
                }
                if ( msg.hasOwnProperty("payload")) {
                    if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist
                        this.brokerConn.publish(msg);  // send the message
                    }
                    else { node.warn(RED._("mqtt.errors.invalid-topic")); }
                }
            });
            if (this.brokerConn.connected) {
                node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
            }
            node.brokerConn.register(node);
            this.on('close', function(done) {
                node.brokerConn.deregister(node,done);
            });
        } else {
            this.error(RED._("mqtt.errors.missing-config"));
        }
    }
    RED.nodes.registerType("mqtt out",MQTTOutNode);
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/21-httpin.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/21-httpin.js

Statements: 12.58% (20 / 159)      Branches: 1.04% (1 / 96)      Functions: 4.76% (1 / 21)      Lines: 13.07% (20 / 153)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311                                1   1 1 1 1 1 1 1 1 1   1                                                                           1   1                                                                                                       1                                                                                   1   1         1                                                                                                                                                                   1     1                                                                                                         1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var bodyParser = require("body-parser");
    var cookieParser = require("cookie-parser");
    var getBody = require('raw-body');
    var cors = require('cors');
    var jsonParser = bodyParser.json();
    var urlencParser = bodyParser.urlencoded({extended:true});
    var onHeaders = require('on-headers');
    var typer = require('media-typer');
    var isUtf8 = require('is-utf8');
 
    function rawBodyParser(req, res, next) {
        if (req.skipRawBodyParser) { next(); } // don't parse this if told to skip
        if (req._body) { return next(); }
        req.body = "";
        req._body = true;
 
        var isText = true;
        var checkUTF = false;
 
        if (req.headers['content-type']) {
            var parsedType = typer.parse(req.headers['content-type'])
            if (parsedType.type === "text") {
                isText = true;
            } else if (parsedType.subtype === "xml" || parsedType.suffix === "xml") {
                isText = true;
            } else if (parsedType.type !== "application") {
                isText = false;
            } else if (parsedType.subtype !== "octet-stream") {
                checkUTF = true;
            } else {
                // applicatino/octet-stream
                isText = false;
            }
        }
 
        getBody(req, {
            length: req.headers['content-length'],
            encoding: isText ? "utf8" : null
        }, function (err, buf) {
            if (err) { return next(err); }
            if (!isText && checkUTF && isUtf8(buf)) {
                buf = buf.toString()
            }
            req.body = buf;
            next();
        });
    }
 
    var corsSetup = false;
 
    function createRequestWrapper(node,req) {
        // This misses a bunch of properties (eg headers). Before we use this function
        // need to ensure it captures everything documented by Express and HTTP modules.
        var wrapper = {
            _req: req
        };
        var toWrap = [
            "param",
            "get",
            "is",
            "acceptsCharset",
            "acceptsLanguage",
            "app",
            "baseUrl",
            "body",
            "cookies",
            "fresh",
            "hostname",
            "ip",
            "ips",
            "originalUrl",
            "params",
            "path",
            "protocol",
            "query",
            "route",
            "secure",
            "signedCookies",
            "stale",
            "subdomains",
            "xhr",
            "socket" // TODO: tidy this up
        ];
        toWrap.forEach(function(f) {
            if (typeof req[f] === "function") {
                wrapper[f] = function() {
                    node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.req."+f}));
                    var result = req[f].apply(req,arguments);
                    if (result === req) {
                        return wrapper;
                    } else {
                        return result;
                    }
                }
            } else {
                wrapper[f] = req[f];
            }
        });
 
 
        return wrapper;
    }
    function createResponseWrapper(node,res) {
        var wrapper = {
            _res: res
        };
        var toWrap = [
            "append",
            "attachment",
            "cookie",
            "clearCookie",
            "download",
            "end",
            "format",
            "get",
            "json",
            "jsonp",
            "links",
            "location",
            "redirect",
            "render",
            "send",
            "sendfile",
            "sendFile",
            "sendStatus",
            "set",
            "status",
            "type",
            "vary"
        ];
        toWrap.forEach(function(f) {
            wrapper[f] = function() {
                node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.res."+f}));
                var result = res[f].apply(res,arguments);
                if (result === res) {
                    return wrapper;
                } else {
                    return result;
                }
            }
        });
        return wrapper;
    }
 
    var corsHandler = function(req,res,next) { next(); }
 
    Iif (RED.settings.httpNodeCors) {
        corsHandler = cors(RED.settings.httpNodeCors);
        RED.httpNode.options("*",corsHandler);
    }
 
    function HTTPIn(n) {
        RED.nodes.createNode(this,n);
        if (RED.settings.httpNodeRoot !== false) {
 
            if (!n.url) {
                this.warn(RED._("httpin.errors.missing-path"));
                return;
            }
            this.url = n.url;
            this.method = n.method;
            this.swaggerDoc = n.swaggerDoc;
 
            var node = this;
 
            this.errorHandler = function(err,req,res,next) {
                node.warn(err);
                res.sendStatus(500);
            };
 
            this.callback = function(req,res) {
                var msgid = RED.util.generateId();
                res._msgid = msgid;
                if (node.method.match(/^(post|delete|put|options|patch)$/)) {
                    node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body});
                } else if (node.method == "get") {
                    node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.query});
                } else {
                    node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res)});
                }
            };
 
            var httpMiddleware = function(req,res,next) { next(); }
 
            if (RED.settings.httpNodeMiddleware) {
                if (typeof RED.settings.httpNodeMiddleware === "function") {
                    httpMiddleware = RED.settings.httpNodeMiddleware;
                }
            }
 
            var metricsHandler = function(req,res,next) { next(); }
            if (this.metric()) {
                metricsHandler = function(req, res, next) {
                    var startAt = process.hrtime();
                    onHeaders(res, function() {
                        if (res._msgid) {
                            var diff = process.hrtime(startAt);
                            var ms = diff[0] * 1e3 + diff[1] * 1e-6;
                            var metricResponseTime = ms.toFixed(3);
                            var metricContentLength = res._headers["content-length"];
                            //assuming that _id has been set for res._metrics in HttpOut node!
                            node.metric("response.time.millis", {_msgid:res._msgid} , metricResponseTime);
                            node.metric("response.content-length.bytes", {_msgid:res._msgid} , metricContentLength);
                        }
                    });
                    next();
                };
            }
 
            if (this.method == "get") {
                RED.httpNode.get(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,this.callback,this.errorHandler);
            } else if (this.method == "post") {
                RED.httpNode.post(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
            } else if (this.method == "put") {
                RED.httpNode.put(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
            } else if (this.method == "patch") {
                RED.httpNode.patch(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
            } else if (this.method == "delete") {
                RED.httpNode.delete(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
            }
 
            this.on("close",function() {
                var node = this;
                RED.httpNode._router.stack.forEach(function(route,i,routes) {
                    if (route.route && route.route.path === node.url && route.route.methods[node.method]) {
                        routes.splice(i,1);
                    }
                });
            });
        } else {
            this.warn(RED._("httpin.errors.not-created"));
        }
    }
    RED.nodes.registerType("http in",HTTPIn);
 
 
    function HTTPOut(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        this.on("input",function(msg) {
            if (msg.res) {
                if (msg.headers) {
                    msg.res._res.set(msg.headers);
                }
                if (msg.cookies) {
                    for (var name in msg.cookies) {
                        if (msg.cookies.hasOwnProperty(name)) {
                            if (msg.cookies[name] === null || msg.cookies[name].value === null) {
                                if (msg.cookies[name]!==null) {
                                    msg.res._res.clearCookie(name,msg.cookies[name]);
                                } else {
                                    msg.res._res.clearCookie(name);
                                }
                            } else if (typeof msg.cookies[name] === 'object') {
                                msg.res._res.cookie(name,msg.cookies[name].value,msg.cookies[name]);
                            } else {
                                msg.res._res.cookie(name,msg.cookies[name]);
                            }
                        }
                    }
                }
                var statusCode = msg.statusCode || 200;
                if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) {
                    msg.res._res.status(statusCode).jsonp(msg.payload);
                } else {
                    if (msg.res._res.get('content-length') == null) {
                        var len;
                        if (msg.payload == null) {
                            len = 0;
                        } else if (Buffer.isBuffer(msg.payload)) {
                            len = msg.payload.length;
                        } else if (typeof msg.payload == "number") {
                            len = Buffer.byteLength(""+msg.payload);
                        } else {
                            len = Buffer.byteLength(msg.payload);
                        }
                        msg.res._res.set('content-length', len);
                    }
 
                    if (typeof msg.payload === "number") {
                        msg.payload = ""+msg.payload;
                    }
                    msg.res._res.status(statusCode).send(msg.payload);
                }
            } else {
                node.warn(RED._("httpin.errors.no-response"));
            }
        });
    }
    RED.nodes.registerType("http response",HTTPOut);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/21-httprequest.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/21-httprequest.js

Statements: 5.48% (8 / 146)      Branches: 0% (0 / 121)      Functions: 10% (1 / 10)      Lines: 5.8% (8 / 138)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223                                1   1 1 1 1 1   1                                                                                                                                                                                                                                                                                                                                                                                           1                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var http = require("follow-redirects").http;
    var https = require("follow-redirects").https;
    var urllib = require("url");
    var mustache = require("mustache");
    var querystring = require("querystring");
 
    function HTTPRequest(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        var nodeUrl = n.url;
        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
        var nodeMethod = n.method || "GET";
        if (n.tls) {
            var tlsNode = RED.nodes.getNode(n.tls);
        }
        this.ret = n.ret || "txt";
        if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; }
        else { this.reqTimeout = 120000; }
 
        var prox, noprox;
        if (process.env.http_proxy != null) { prox = process.env.http_proxy; }
        if (process.env.HTTP_PROXY != null) { prox = process.env.HTTP_PROXY; }
        if (process.env.no_proxy != null) { noprox = process.env.no_proxy.split(","); }
        if (process.env.NO_PROXY != null) { noprox = process.env.NO_PROXY.split(","); }
 
        this.on("input",function(msg) {
            var preRequestTimestamp = process.hrtime();
            node.status({fill:"blue",shape:"dot",text:"httpin.status.requesting"});
            var url = nodeUrl || msg.url;
            if (msg.url && nodeUrl && (nodeUrl !== msg.url)) {  // revert change below when warning is finally removed
                node.warn(RED._("common.errors.nooverride"));
            }
            if (isTemplatedUrl) {
                url = mustache.render(nodeUrl,msg);
            }
            if (!url) {
                node.error(RED._("httpin.errors.no-url"),msg);
                return;
            }
            // url must start http:// or https:// so assume http:// if not set
            if (!((url.indexOf("http://") === 0) || (url.indexOf("https://") === 0))) {
                if (tlsNode) {
                    url = "https://"+url;
                } else {
                    url = "http://"+url;
                }
            }
 
            var method = nodeMethod.toUpperCase() || "GET";
            if (msg.method && n.method && (n.method !== "use")) {     // warn if override option not set
                node.warn(RED._("common.errors.nooverride"));
            }
            if (msg.method && n.method && (n.method === "use")) {
                method = msg.method.toUpperCase();          // use the msg parameter
            }
            var opts = urllib.parse(url);
            opts.method = method;
            opts.headers = {};
            var ctSet = "Content-Type"; // set default camel case
            var clSet = "Content-Length";
            if (msg.headers) {
                for (var v in msg.headers) {
                    if (msg.headers.hasOwnProperty(v)) {
                        var name = v.toLowerCase();
                        if (name !== "content-type" && name !== "content-length") {
                            // only normalise the known headers used later in this
                            // function. Otherwise leave them alone.
                            name = v;
                        }
                        else if (name === 'content-type') { ctSet = v; }
                        else { clSet = v; }
                        opts.headers[name] = msg.headers[v];
                    }
                }
            }
            if (this.credentials && this.credentials.user) {
                opts.auth = this.credentials.user+":"+(this.credentials.password||"");
            }
            var payload = null;
 
            if (msg.payload && (method == "POST" || method == "PUT" || method == "PATCH" ) ) {
                if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
                    payload = msg.payload;
                } else if (typeof msg.payload == "number") {
                    payload = msg.payload+"";
                } else {
                    if (opts.headers['content-type'] == 'application/x-www-form-urlencoded') {
                        payload = querystring.stringify(msg.payload);
                    } else {
                        payload = JSON.stringify(msg.payload);
                        if (opts.headers['content-type'] == null) {
                            opts.headers[ctSet] = "application/json";
                        }
                    }
                }
                if (opts.headers['content-length'] == null) {
                    if (Buffer.isBuffer(payload)) {
                        opts.headers[clSet] = payload.length;
                    } else {
                        opts.headers[clSet] = Buffer.byteLength(payload);
                    }
                }
            }
            // revert to user supplied Capitalisation if needed.
            if (opts.headers.hasOwnProperty('content-type') && (ctSet !== 'content-type')) {
                opts.headers[ctSet] = opts.headers['content-type'];
                delete opts.headers['content-type'];
            }
            if (opts.headers.hasOwnProperty('content-length') && (clSet !== 'content-length')) {
                opts.headers[clSet] = opts.headers['content-length'];
                delete opts.headers['content-length'];
            }
            var urltotest = url;
            var noproxy;
            if (noprox) {
                for (var i in noprox) {
                    if (url.indexOf(noprox[i]) !== -1) { noproxy=true; }
                }
            }
            if (prox && !noproxy) {
                var match = prox.match(/^(http:\/\/)?(.+)?:([0-9]+)?/i);
                if (match) {
                    //opts.protocol = "http:";
                    //opts.host = opts.hostname = match[2];
                    //opts.port = (match[3] != null ? match[3] : 80);
                    opts.headers['Host'] = opts.host;
                    var heads = opts.headers;
                    var path = opts.pathname = opts.href;
                    opts = urllib.parse(prox);
                    opts.path = opts.pathname = path;
                    opts.headers = heads;
                    opts.method = method;
                    urltotest = match[0];
                }
                else { node.warn("Bad proxy url: "+process.env.http_proxy); }
            }
            if (tlsNode) {
                tlsNode.addTLSOptions(opts);
            }
            var req = ((/^https/.test(urltotest))?https:http).request(opts,function(res) {
                (node.ret === "bin") ? res.setEncoding('binary') : res.setEncoding('utf8');
                msg.statusCode = res.statusCode;
                msg.headers = res.headers;
                msg.responseUrl = res.responseUrl;
                msg.payload = "";
                // msg.url = url;   // revert when warning above finally removed
                res.on('data',function(chunk) {
                    msg.payload += chunk;
                });
                res.on('end',function() {
                    if (node.metric()) {
                        // Calculate request time
                        var diff = process.hrtime(preRequestTimestamp);
                        var ms = diff[0] * 1e3 + diff[1] * 1e-6;
                        var metricRequestDurationMillis = ms.toFixed(3);
                        node.metric("duration.millis", msg, metricRequestDurationMillis);
                        if (res.client && res.client.bytesRead) {
                            node.metric("size.bytes", msg, res.client.bytesRead);
                        }
                    }
                    if (node.ret === "bin") {
                        msg.payload = new Buffer(msg.payload,"binary");
                    }
                    else if (node.ret === "obj") {
                        try { msg.payload = JSON.parse(msg.payload); }
                        catch(e) { node.warn(RED._("httpin.errors.json-error")); }
                    }
                    node.send(msg);
                    node.status({});
                });
            });
            req.setTimeout(node.reqTimeout, function() {
                node.error(RED._("common.notification.errors.no-response"),msg);
                setTimeout(function() {
                    node.status({fill:"red",shape:"ring",text:"common.notification.errors.no-response"});
                },10);
                req.abort();
            });
            req.on('error',function(err) {
                node.error(err,msg);
                msg.payload = err.toString() + " : " + url;
                msg.statusCode = err.code;
                node.send(msg);
                node.status({fill:"red",shape:"ring",text:err.code});
            });
            if (payload) {
                req.write(payload);
            }
            req.end();
        });
 
        this.on("close",function() {
            node.status({});
        });
    }
 
    RED.nodes.registerType("http request",HTTPRequest,{
        credentials: {
            user: {type:"text"},
            password: {type: "password"}
        }
    });
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/22-websocket.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/22-websocket.js

Statements: 11.11% (17 / 153)      Branches: 0% (0 / 65)      Functions: 3.33% (1 / 30)      Lines: 12.14% (17 / 140)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267                                1   1 1     1                             1               1                                                                                                                                                                                     1 1   1       1               1                                       1                                 1                     1                                     1   1                                                                                     1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var ws = require("ws");
    var inspect = require("util").inspect;
 
    // A node red node that sets up a local websocket server
    function WebSocketListenerNode(n) {
        // Create a RED node
        RED.nodes.createNode(this,n);
        var node = this;
 
        // Store local copies of the node configuration (as defined in the .html)
        node.path = n.path;
        node.wholemsg = (n.wholemsg === "true");
 
        node._inputNodes = [];    // collection of nodes that want to receive events
        node._clients = {};
        // match absolute url
        node.isServer = !/^ws{1,2}:\/\//i.test(node.path);
        node.closing = false;
 
        function startconn() {    // Connect to remote endpoint
            node.tout = null;
            var socket = new ws(node.path);
            socket.setMaxListeners(0);
            node.server = socket; // keep for closing
            handleConnection(socket);
        }
 
        function handleConnection(/*socket*/socket) {
            var id = (1+Math.random()*4294967295).toString(16);
            if (node.isServer) { node._clients[id] = socket; node.emit('opened',Object.keys(node._clients).length); }
            socket.on('open',function() {
                if (!node.isServer) { node.emit('opened',''); }
            });
            socket.on('close',function() {
                if (node.isServer) { delete node._clients[id]; node.emit('closed',Object.keys(node._clients).length); }
                else { node.emit('closed'); }
                if (!node.closing && !node.isServer) {
                    clearTimeout(node.tout);
                    node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
                }
            });
            socket.on('message',function(data,flags) {
                node.handleEvent(id,socket,'message',data,flags);
            });
            socket.on('error', function(err) {
                node.emit('erro');
                if (!node.closing && !node.isServer) {
                    clearTimeout(node.tout);
                    node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
                }
            });
        }
 
        if (node.isServer) {
            var path = RED.settings.httpNodeRoot || "/";
            path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
 
            // Workaround https://github.com/einaros/ws/pull/253
            // Listen for 'newListener' events from RED.server
            node._serverListeners = {};
 
            var storeListener = function(/*String*/event,/*function*/listener) {
                if (event == "error" || event == "upgrade" || event == "listening") {
                    node._serverListeners[event] = listener;
                }
            }
 
            RED.server.addListener('newListener',storeListener);
 
            // Create a WebSocket Server
            node.server = new ws.Server({
                server:RED.server,
                path:path,
                // Disable the deflate option due to this issue
                //  https://github.com/websockets/ws/pull/632
                // that is fixed in the 1.x release of the ws module
                // that we cannot currently pickup as it drops node 0.10 support
                perMessageDeflate: false
            });
 
            // Workaround https://github.com/einaros/ws/pull/253
            // Stop listening for new listener events
            RED.server.removeListener('newListener',storeListener);
            node.server.setMaxListeners(0);
            node.server.on('connection', handleConnection);
        }
        else {
            node.closing = false;
            startconn(); // start outbound connection
        }
 
        node.on("close", function() {
            // Workaround https://github.com/einaros/ws/pull/253
            // Remove listeners from RED.server
            if (node.isServer) {
                var listener = null;
                for (var event in node._serverListeners) {
                    if (node._serverListeners.hasOwnProperty(event)) {
                        listener = node._serverListeners[event];
                        if (typeof listener === "function") {
                            RED.server.removeListener(event,listener);
                        }
                    }
                }
                node._serverListeners = {};
                node.server.close();
                node._inputNodes = [];
            }
            else {
                node.closing = true;
                node.server.close();
                if (node.tout) {
                    clearTimeout(node.tout);
                    node.tout = null;
                }
            }
        });
    }
    RED.nodes.registerType("websocket-listener",WebSocketListenerNode);
    RED.nodes.registerType("websocket-client",WebSocketListenerNode);
 
    WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) {
        this._inputNodes.push(handler);
    }
 
    WebSocketListenerNode.prototype.removeInputNode = function(/*Node*/handler) {
        this._inputNodes.forEach(function(node, i, inputNodes) {
            if (node === handler) {
                inputNodes.splice(i, 1);
            }
        });
    }
 
    WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags) {
        var msg;
        if (this.wholemsg) {
            try {
                msg = JSON.parse(data);
            }
            catch(err) {
                msg = { payload:data };
            }
        } else {
            msg = {
                payload:data
            };
        }
        msg._session = {type:"websocket",id:id};
        for (var i = 0; i < this._inputNodes.length; i++) {
            this._inputNodes[i].send(msg);
        }
    }
 
    WebSocketListenerNode.prototype.broadcast = function(data) {
        var i;
        try {
            if (this.isServer) {
                for (i = 0; i < this.server.clients.length; i++) {
                    this.server.clients[i].send(data);
                }
            }
            else {
                this.server.send(data);
            }
        }
        catch(e) { // swallow any errors
            this.warn("ws:"+i+" : "+e);
        }
    }
 
    WebSocketListenerNode.prototype.reply = function(id,data) {
        var session = this._clients[id];
        if (session) {
            try {
                session.send(data);
            }
            catch(e) { // swallow any errors
            }
        }
    }
 
    function WebSocketInNode(n) {
        RED.nodes.createNode(this,n);
        this.server = (n.client)?n.client:n.server;
        var node = this;
        this.serverConfig = RED.nodes.getNode(this.server);
        if (this.serverConfig) {
            this.serverConfig.registerInputNode(this);
            // TODO: nls
            this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); });
            this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); });
            this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
        } else {
            this.error(RED._("websocket.errors.missing-conf"));
        }
        this.on('close', function() {
            node.serverConfig.removeInputNode(node);
            node.status({});
        });
    }
    RED.nodes.registerType("websocket in",WebSocketInNode);
 
    function WebSocketOutNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        this.server = (n.client)?n.client:n.server;
        this.serverConfig = RED.nodes.getNode(this.server);
        if (!this.serverConfig) {
            this.error(RED._("websocket.errors.missing-conf"));
        }
        else {
            // TODO: nls
            this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:"connected "+n}); });
            this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"error"}); });
            this.serverConfig.on('closed', function() { node.status({fill:"red",shape:"ring",text:"disconnected"}); });
        }
        this.on("input", function(msg) {
            var payload;
            if (this.serverConfig.wholemsg) {
                delete msg._session;
                payload = JSON.stringify(msg);
            } else if (msg.hasOwnProperty("payload")) {
                if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string.
                    payload = RED.util.ensureString(msg.payload);
                }
                else {
                    payload = msg.payload;
                }
            }
            if (payload) {
                if (msg._session && msg._session.type == "websocket") {
                    node.serverConfig.reply(msg._session.id,payload);
                } else {
                    node.serverConfig.broadcast(payload,function(error) {
                        if (!!error) {
                            node.warn(RED._("websocket.errors.send-error")+inspect(error));
                        }
                    });
                }
            }
        });
        this.on('close', function() {
            node.status({});
        });
    }
    RED.nodes.registerType("websocket out",WebSocketOutNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/23-watch.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/23-watch.js

Statements: 13.95% (6 / 43)      Branches: 0% (0 / 20)      Functions: 20% (1 / 5)      Lines: 17.14% (6 / 35)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67                                1   1 1 1   1                                                                                 1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var Notify = require("fs.notify");
    var fs = require("fs");
    var sep = require("path").sep;
 
    function WatchNode(n) {
        RED.nodes.createNode(this,n);
 
        this.files = (n.files || "").split(",");
        for (var f=0; f < this.files.length; f++) {
            this.files[f] = this.files[f].trim();
        }
        this.p = (this.files.length === 1) ? this.files[0] : JSON.stringify(this.files);
        var node = this;
 
        var notifications = new Notify(node.files);
        notifications.on('change', function (file, event, path) {
            var stat;
            try {
                if (fs.statSync(path).isDirectory()) { path = path + sep + file; }
                stat = fs.statSync(path);
            } catch(e) { }
            var type = "none";
            var msg = { payload:path, topic:node.p, file:file };
            if (stat) {
                if (stat.isFile()) { type = "file"; msg.size = stat.size; }
                else if (stat.isDirectory()) { type = "directory"; }
                else if (stat.isBlockDevice()) { type = "blockdevice"; }
                else if (stat.isCharacterDevice()) { type = "characterdevice"; }
                else if (stat.isSocket()) { type = "socket"; }
                else if (stat.isFIFO()) { type = "fifo"; }
                else { type = "n/a"; }
            }
            msg.type = type;
            node.send(msg);
        });
 
        notifications.on('error', function (error, path) {
            var msg = { payload:path };
            node.error(error,msg);
        });
 
        this.close = function() {
            notifications.close();
        }
    }
    RED.nodes.registerType("watch",WatchNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/31-tcpin.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/31-tcpin.js

Statements: 2.7% (11 / 408)      Branches: 1.72% (4 / 233)      Functions: 2.17% (1 / 46)      Lines: 2.81% (11 / 391)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617                                1   1 1 1   1   1                                                                                                                                                                                                                                                                                                                                                                                         1   1                                                                                                                                                                                                                                                                                                                                                   1   1                                                                                                                                                                                                                                                                                                                                                                                                                                                                   1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var reconnectTime = RED.settings.socketReconnectTime||10000;
    var socketTimeout = RED.settings.socketTimeout||null;
    var net = require('net');
 
    var connectionPool = {};
 
    function TcpIn(n) {
        RED.nodes.createNode(this,n);
        this.host = n.host;
        this.port = n.port * 1;
        this.topic = n.topic;
        this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
        this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
        this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r");
        this.base64 = n.base64;
        this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");
        this.closing = false;
        this.connected = false;
        var node = this;
        var count = 0;
 
        if (!node.server) {
            var buffer = null;
            var client;
            var reconnectTimeout;
            var end = false;
            var setupTcpClient = function() {
                node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
                node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
                var id = (1+Math.random()*4294967295).toString(16);
                client = net.connect(node.port, node.host, function() {
                    buffer = (node.datatype == 'buffer')? new Buffer(0):"";
                    node.connected = true;
                    node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port}));
                    node.status({fill:"green",shape:"dot",text:"common.status.connected"});
                });
                connectionPool[id] = client;
 
                client.on('data', function (data) {
                    if (node.datatype != 'buffer') {
                        data = data.toString(node.datatype);
                    }
                    if (node.stream) {
                        var msg;
                        if ((node.datatype) === "utf8" && node.newline !== "") {
                            buffer = buffer+data;
                            var parts = buffer.split(node.newline);
                            for (var i = 0;i<parts.length-1;i+=1) {
                                msg = {topic:node.topic, payload:parts[i]};
                                msg._session = {type:"tcp",id:id};
                                node.send(msg);
                            }
                            buffer = parts[parts.length-1];
                        } else {
                            msg = {topic:node.topic, payload:data};
                            msg._session = {type:"tcp",id:id};
                            node.send(msg);
                        }
                    } else {
                        if ((typeof data) === "string") {
                            buffer = buffer+data;
                        } else {
                            buffer = Buffer.concat([buffer,data],buffer.length+data.length);
                        }
                    }
                });
                client.on('end', function() {
                    if (!node.stream || (node.datatype == "utf8" && node.newline !== "" && buffer.length > 0)) {
                        var msg = {topic:node.topic, payload:buffer};
                        msg._session = {type:"tcp",id:id};
                        if (buffer.length !== 0) {
                            end = true; // only ask for fast re-connect if we actually got something
                            node.send(msg);
                        }
                        buffer = null;
                    }
                });
                client.on('close', function() {
                    delete connectionPool[id];
                    node.connected = false;
                    node.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
                    if (!node.closing) {
                        if (end) { // if we were asked to close then try to reconnect once very quick.
                            end = false;
                            reconnectTimeout = setTimeout(setupTcpClient, 20);
                        }
                        else {
                            node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port}));
                            reconnectTimeout = setTimeout(setupTcpClient, reconnectTime);
                        }
                    } else {
                        if (node.done) { node.done(); }
                    }
                });
                client.on('error', function(err) {
                    node.log(err);
                });
            }
            setupTcpClient();
 
            this.on('close', function(done) {
                node.done = done;
                this.closing = true;
                if (client) { client.destroy(); }
                clearTimeout(reconnectTimeout);
                if (!node.connected) { done(); }
            });
        } else {
            var server = net.createServer(function (socket) {
                socket.setKeepAlive(true,120000);
                if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
                var id = (1+Math.random()*4294967295).toString(16);
                connectionPool[id] = socket;
                count++;
                node.status({text:RED._("tcpin.status.connections",{count:count})});
 
                var buffer = (node.datatype == 'buffer')? new Buffer(0):"";
                socket.on('data', function (data) {
                    if (node.datatype != 'buffer') {
                        data = data.toString(node.datatype);
                    }
                    if (node.stream) {
                        var msg;
                        if ((typeof data) === "string" && node.newline !== "") {
                            buffer = buffer+data;
                            var parts = buffer.split(node.newline);
                            for (var i = 0; i<parts.length-1; i+=1) {
                                msg = {topic:node.topic, payload:parts[i],ip:socket.remoteAddress,port:socket.remotePort};
                                msg._session = {type:"tcp",id:id};
                                node.send(msg);
                            }
                            buffer = parts[parts.length-1];
                        } else {
                            msg = {topic:node.topic, payload:data};
                            msg._session = {type:"tcp",id:id};
                            node.send(msg);
                        }
                    } else {
                        if ((typeof data) === "string") {
                            buffer = buffer+data;
                        } else {
                            buffer = Buffer.concat([buffer,data],buffer.length+data.length);
                        }
                    }
                });
                socket.on('end', function() {
                    if (!node.stream || (node.datatype === "utf8" && node.newline !== "")) {
                        if (buffer.length > 0) {
                            var msg = {topic:node.topic, payload:buffer};
                            msg._session = {type:"tcp",id:id};
                            node.send(msg);
                        }
                        buffer = null;
                    }
                });
                socket.on('timeout', function() {
                    node.log(RED._("tcpin.errors.timeout",{port:node.port}));
                    socket.end();
                });
                socket.on('close', function() {
                    delete connectionPool[id];
                    count--;
                    node.status({text:RED._("tcpin.status.connections",{count:count})});
                });
                socket.on('error',function(err) {
                    node.log(err);
                });
            });
            server.on('error', function(err) {
                if (err) {
                    node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()}));
                }
            });
 
            server.listen(node.port, function(err) {
                if (err) {
                    node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()}));
                } else {
                    node.log(RED._("tcpin.status.listening-port",{port:node.port}));
                    node.on('close', function() {
                        for (var c in connectionPool) {
                            if (connectionPool.hasOwnProperty(c)) {
                                connectionPool[c].end();
                                connectionPool[c].unref();
                            }
                        }
                        node.closing = true;
                        server.close();
                        node.log(RED._("tcpin.status.stopped-listening",{port:node.port}));
                    });
                }
            });
        }
 
    }
    RED.nodes.registerType("tcp in",TcpIn);
 
    function TcpOut(n) {
        RED.nodes.createNode(this,n);
        this.host = n.host;
        this.port = n.port * 1;
        this.base64 = n.base64;
        this.doend = n.end || false;
        this.beserver = n.beserver;
        this.name = n.name;
        this.closing = false;
        this.connected = false;
        var node = this;
 
        if (!node.beserver||node.beserver=="client") {
            var reconnectTimeout;
            var client = null;
            var end = false;
 
            var setupTcpClient = function() {
                node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port}));
                node.status({fill:"grey",shape:"dot",text:"common.status.connecting"});
                client = net.connect(node.port, node.host, function() {
                    node.connected = true;
                    node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port}));
                    node.status({fill:"green",shape:"dot",text:"common.status.connected"});
                });
                client.on('error', function (err) {
                    node.log(RED._("tcpin.errors.error",{error:err.toString()}));
                });
                client.on('end', function (err) {
                    node.status({});
                    node.connected = false;
                });
                client.on('close', function() {
                    node.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
                    node.connected = false;
                    client.destroy();
                    if (!node.closing) {
                        if (end) {
                            end = false;
                            reconnectTimeout = setTimeout(setupTcpClient,20);
                        }
                        else {
                            node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port}));
                            reconnectTimeout = setTimeout(setupTcpClient,reconnectTime);
                        }
                    } else {
                        if (node.done) { node.done(); }
                    }
                });
            }
            setupTcpClient();
 
            node.on("input", function(msg) {
                if (node.connected && msg.payload != null) {
                    if (Buffer.isBuffer(msg.payload)) {
                        client.write(msg.payload);
                    } else if (typeof msg.payload === "string" && node.base64) {
                        client.write(new Buffer(msg.payload,'base64'));
                    } else {
                        client.write(new Buffer(""+msg.payload));
                    }
                    if (node.doend === true) {
                        end = true;
                        if (client) { node.status({}); client.destroy(); }
                    }
                }
            });
 
            node.on("close", function(done) {
                node.done = done;
                this.closing = true;
                if (client) { client.destroy(); }
                clearTimeout(reconnectTimeout);
                if (!node.connected) { done(); }
            });
 
        } else if (node.beserver == "reply") {
            node.on("input",function(msg) {
                if (msg._session && msg._session.type == "tcp") {
                    var client = connectionPool[msg._session.id];
                    if (client) {
                        if (Buffer.isBuffer(msg.payload)) {
                            client.write(msg.payload);
                        } else if (typeof msg.payload === "string" && node.base64) {
                            client.write(new Buffer(msg.payload,'base64'));
                        } else {
                            client.write(new Buffer(""+msg.payload));
                        }
                    }
                }
                else {
                    for (var i in connectionPool) {
                        if (Buffer.isBuffer(msg.payload)) {
                            connectionPool[i].write(msg.payload);
                        } else if (typeof msg.payload === "string" && node.base64) {
                            connectionPool[i].write(new Buffer(msg.payload,'base64'));
                        } else {
                            connectionPool[i].write(new Buffer(""+msg.payload));
                        }
                    }
                }
            });
        } else {
            var connectedSockets = [];
            node.status({text:RED._("tcpin.status.connections",{count:0})});
            var server = net.createServer(function (socket) {
                socket.setKeepAlive(true,120000);
                if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
                var remoteDetails = socket.remoteAddress+":"+socket.remotePort;
                node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort}));
                connectedSockets.push(socket);
                node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
                socket.on('timeout', function() {
                    node.log(RED._("tcpin.errors.timeout",{port:node.port}));
                    socket.end();
                });
                socket.on('close',function() {
                    node.log(RED._("tcpin.status.connection-closed",{host:socket.remoteAddress, port:socket.remotePort}));
                    connectedSockets.splice(connectedSockets.indexOf(socket),1);
                    node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
                });
                socket.on('error',function() {
                    node.log(RED._("tcpin.errors.socket-error",{host:socket.remoteAddress, port:socket.remotePort}));
                    connectedSockets.splice(connectedSockets.indexOf(socket),1);
                    node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
                });
            });
 
            node.on("input", function(msg) {
                if (msg.payload != null) {
                    var buffer;
                    if (Buffer.isBuffer(msg.payload)) {
                        buffer = msg.payload;
                    } else if (typeof msg.payload === "string" && node.base64) {
                        buffer = new Buffer(msg.payload,'base64');
                    } else {
                        buffer = new Buffer(""+msg.payload);
                    }
                    for (var i = 0; i < connectedSockets.length; i += 1) {
                        if (node.doend === true) { connectedSockets[i].end(buffer); }
                        else { connectedSockets[i].write(buffer); }
                    }
                }
            });
 
            server.on('error', function(err) {
                if (err) {
                    node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()}));
                }
            });
 
            server.listen(node.port, function(err) {
                if (err) {
                    node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()}));
                } else {
                    node.log(RED._("tcpin.status.listening-port",{port:node.port}));
                    node.on('close', function() {
                        for (var c in connectedSockets) {
                            if (connectedSockets.hasOwnProperty(c)) {
                                connectedSockets[c].end();
                                connectedSockets[c].unref();
                            }
                        }
                        server.close();
                        node.log(RED._("tcpin.status.stopped-listening",{port:node.port}));
                    });
                }
            });
        }
    }
    RED.nodes.registerType("tcp out",TcpOut);
 
    function TcpGet(n) {
        RED.nodes.createNode(this,n);
        this.server = n.server;
        this.port = Number(n.port);
        this.out = n.out;
        this.splitc = n.splitc;
 
        if (this.out != "char") { this.splitc = Number(this.splitc); }
        else {
            if (this.splitc[0] == '\\') {
                this.splitc = parseInt(this.splitc.replace("\\n",0x0A).replace("\\r",0x0D).replace("\\t",0x09).replace("\\e",0x1B).replace("\\f",0x0C).replace("\\0",0x00));
            } // jshint ignore:line
            if (typeof this.splitc == "string") {
                if (this.splitc.substr(0,2) == "0x") {
                    this.splitc = parseInt(this.splitc);
                }
                else {
                    this.splitc = this.splitc.charCodeAt(0);
                }
            } // jshint ignore:line
        }
 
        var node = this;
 
        var clients = {};
 
        this.on("input", function(msg) {
            var i = 0;
            if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
                msg.payload = msg.payload.toString();
            }
 
            var host = node.server || msg.host;
            var port = node.port || msg.port;
 
            // Store client information independently
            // the clients object will have:
            // clients[id].client, clients[id].msg, clients[id].timeout
            var connection_id = host + ":" + port;
            clients[connection_id] = clients[connection_id] || {};
            clients[connection_id].msg = msg;
            clients[connection_id].connected = clients[connection_id].connected || false;
 
            if (!clients[connection_id].connected) {
                var buf;
                if (this.out == "count") {
                    if (this.splitc === 0) { buf = new Buffer(1); }
                    else { buf = new Buffer(this.splitc); }
                }
                else { buf = new Buffer(65536); } // set it to 64k... hopefully big enough for most TCP packets.... but only hopefully
 
                clients[connection_id].client = net.Socket();
                if (socketTimeout !== null) { clients[connection_id].client.setTimeout(socketTimeout);}
 
                if (host && port) {
                    clients[connection_id].client.connect(port, host, function() {
                        //node.log(RED._("tcpin.errors.client-connected"));
                        node.status({fill:"green",shape:"dot",text:"common.status.connected"});
                        if (clients[connection_id] && clients[connection_id].client) {
                            clients[connection_id].connected  = true;
                            clients[connection_id].client.write(clients[connection_id].msg.payload);
                        }
                    });
                }
                else {
                    node.warn(RED._("tcpin.errors.no-host"));
                }
 
                clients[connection_id].client.on('data', function(data) {
                    if (node.out == "sit") { // if we are staying connected just send the buffer
                        if (clients[connection_id]) {
                            clients[connection_id].msg.payload = data;
                            node.send(clients[connection_id].msg);
                        }
                    }
                    else if (node.splitc === 0) {
                        clients[connection_id].msg.payload = data;
                        node.send(clients[connection_id].msg);
                    }
                    else {
                        for (var j = 0; j < data.length; j++ ) {
                            if (node.out === "time") {
                                if (clients[connection_id]) {
                                    // do the timer thing
                                    if (clients[connection_id].timeout) {
                                        i += 1;
                                        buf[i] = data[j];
                                    }
                                    else {
                                        clients[connection_id].timeout = setTimeout(function () {
                                            if (clients[connection_id]) {
                                                clients[connection_id].timeout = null;
                                                clients[connection_id].msg.payload = new Buffer(i+1);
                                                buf.copy(clients[connection_id].msg.payload,0,0,i+1);
                                                node.send(clients[connection_id].msg);
                                                if (clients[connection_id].client) {
                                                    node.status({}); clients[connection_id].client.destroy();
                                                    delete clients[connection_id];
                                                }
                                            }
                                        }, node.splitc);
                                        i = 0;
                                        buf[0] = data[j];
                                    }
                                }
                            }
                            // count bytes into a buffer...
                            else if (node.out == "count") {
                                buf[i] = data[j];
                                i += 1;
                                if ( i >= node.splitc) {
                                    if (clients[connection_id]) {
                                        clients[connection_id].msg.payload = new Buffer(i);
                                        buf.copy(clients[connection_id].msg.payload,0,0,i);
                                        node.send(clients[connection_id].msg);
                                        if (clients[connection_id].client) {
                                            node.status({}); clients[connection_id].client.destroy();
                                            delete clients[connection_id];
                                        }
                                        i = 0;
                                    }
                                }
                            }
                            // look for a char
                            else {
                                buf[i] = data[j];
                                i += 1;
                                if (data[j] == node.splitc) {
                                    if (clients[connection_id]) {
                                        clients[connection_id].msg.payload = new Buffer(i);
                                        buf.copy(clients[connection_id].msg.payload,0,0,i);
                                        node.send(clients[connection_id].msg);
                                        if (clients[connection_id].client) {
                                            node.status({}); clients[connection_id].client.destroy();
                                            delete clients[connection_id];
                                        }
                                        i = 0;
                                    }
                                }
                            }
                        }
                    }
                });
 
                clients[connection_id].client.on('end', function() {
                    //console.log("END");
                    node.status({fill:"grey",shape:"ring",text:"common.status.disconnected"});
                    if (clients[connection_id] && clients[connection_id].client) {
                        clients[connection_id].connected  = false;
                        clients[connection_id].client = null;
                    }
                });
 
                clients[connection_id].client.on('close', function() {
                    //console.log("CLOSE");
                    if (clients[connection_id]) {
                        clients[connection_id].connected  = false;
                    }
 
                    var anyConnected = false;
 
                    for (var client in clients) {
                        if (clients[client].connected) {
                            anyConnected = true;
                            break;
                        }
                    }
                    if (node.done && !anyConnected) {
                        clients = {};
                        node.done();
                    }
                });
 
                clients[connection_id].client.on('error', function() {
                    //console.log("ERROR");
                    node.status({fill:"red",shape:"ring",text:"common.status.error"});
                    node.error(RED._("tcpin.errors.connect-fail") + " " + connection_id, msg);
                    if (clients[connection_id] && clients[connection_id].client) {
                        clients[connection_id].connected = false;
                        clients[connection_id].client.destroy();
                        delete clients[connection_id];
                    }
                });
 
                clients[connection_id].client.on('timeout',function() {
                    //console.log("TIMEOUT");
                    clients[connection_id].connected = false;
                    node.status({fill:"grey",shape:"dot",text:"tcpin.errors.connect-timeout"});
                    //node.warn(RED._("tcpin.errors.connect-timeout"));
                    if (clients[connection_id] && clients[connection_id].client) {
                        clients[connection_id].client.connect(port, host, function() {
                            clients[connection_id].connected = true;
                            node.status({fill:"green",shape:"dot",text:"common.status.connected"});
                        });
                    }
                });
            }
            else {
                if (clients[connection_id] && clients[connection_id].client) {
                    clients[connection_id].client.write(clients[connection_id].msg.payload);
                }
            }
        });
 
        this.on("close", function(done) {
            node.done = done;
            for (var client in clients) {
                clients[client].client.destroy();
            }
            node.status({});
 
            var anyConnected = false;
            for (var c in clients) {
                if (clients[c].connected) {
                    anyConnected = true;
                    break;
                }
            }
 
            if (!anyConnected) {
                clients = {};
                done();
            }
        });
 
    }
    RED.nodes.registerType("tcp request",TcpGet);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/32-udp.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/io/32-udp.js

Statements: 6.3% (8 / 127)      Branches: 0% (0 / 75)      Functions: 7.69% (1 / 13)      Lines: 6.56% (8 / 122)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215                                1   1 1     1                                                                                                                                                                   1     1       1                                                                                                                                                                                                       1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var dgram = require('dgram');
    var udpInputPortsInUse = {};
 
    // The Input Node
    function UDPin(n) {
        RED.nodes.createNode(this,n);
        this.group = n.group;
        this.port = n.port;
        this.datatype = n.datatype;
        this.iface = n.iface || null;
        this.multicast = n.multicast;
        this.ipv = n.ipv || "udp4";
        var node = this;
 
        var opts = {type:node.ipv, reuseAddr:true};
        if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
        var server;
 
        if (!udpInputPortsInUse.hasOwnProperty(this.port)) {
            server = dgram.createSocket(opts);  // default to udp4
            udpInputPortsInUse[this.port] = server;
        }
        else {
            node.warn(RED._("udp.errors.alreadyused",node.port));
            server = udpInputPortsInUse[this.port];  // re-use existing
        }
 
        if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
 
        server.on("error", function (err) {
            if ((err.code == "EACCES") && (node.port < 1024)) {
                node.error(RED._("udp.errors.access-error"));
            } else {
                node.error(RED._("udp.errors.error",{error:err.code}));
            }
            server.close();
        });
 
        server.on('message', function (message, remote) {
            var msg;
            if (node.datatype =="base64") {
                msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
            } else if (node.datatype =="utf8") {
                msg = { payload:message.toString('utf8'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
            } else {
                msg = { payload:message, fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
            }
            node.send(msg);
        });
 
        server.on('listening', function () {
            var address = server.address();
            node.log(RED._("udp.status.listener-at",{host:address.address,port:address.port}));
            if (node.multicast == "true") {
                server.setBroadcast(true);
                try {
                    server.setMulticastTTL(128);
                    server.addMembership(node.group,node.iface);
                    node.log(RED._("udp.status.mc-group",{group:node.group}));
                } catch (e) {
                    if (e.errno == "EINVAL") {
                        node.error(RED._("udp.errors.bad-mcaddress"));
                    } else if (e.errno == "ENODEV") {
                        node.error(RED._("udp.errors.interface"));
                    } else {
                        node.error(RED._("udp.errors.error",{error:e.errno}));
                    }
                }
            }
        });
 
        node.on("close", function() {
            if (udpInputPortsInUse.hasOwnProperty(node.port)) {
                delete udpInputPortsInUse[node.port];
            }
            try {
                server.close();
                node.log(RED._("udp.status.listener-stopped"));
            } catch (err) {
                //node.error(err);
            }
        });
 
        try { server.bind(node.port,node.iface); }
        catch(e) { } // Don't worry if already bound
    }
    RED.httpAdmin.get('/udp-ports/:id', RED.auth.needsPermission('udp-ports.read'), function(req,res) {
        res.json(Object.keys(udpInputPortsInUse));
    });
    RED.nodes.registerType("udp in",UDPin);
 
 
    // The Output Node
    function UDPout(n) {
        RED.nodes.createNode(this,n);
        //this.group = n.group;
        this.port = n.port;
        this.outport = n.outport||"";
        this.base64 = n.base64;
        this.addr = n.addr;
        this.iface = n.iface || null;
        this.multicast = n.multicast;
        this.ipv = n.ipv || "udp4";
        var node = this;
 
        var opts = {type:node.ipv, reuseAddr:true};
        if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
 
        var sock;
        if (udpInputPortsInUse[this.outport]) {
            sock = udpInputPortsInUse[this.outport];
        }
        else {
            sock = dgram.createSocket(opts);  // default to udp4
            udpInputPortsInUse[this.outport] = sock;
        }
 
        sock.on("error", function(err) {
            // Any async error will also get reported in the sock.send call.
            // This handler is needed to ensure the error marked as handled to
            // prevent it going to the global error handler and shutting node-red
            // down.
        });
        if (node.multicast != "false") {
            if (node.outport === "") { node.outport = node.port; }
            sock.bind(node.outport, function() {    // have to bind before you can enable broadcast...
                sock.setBroadcast(true);            // turn on broadcast
                if (node.multicast == "multi") {
                    try {
                        sock.setMulticastTTL(128);
                        sock.addMembership(node.addr,node.iface);   // Add to the multicast group
                        node.log(RED._("udp.status.mc-ready",{outport:node.outport,host:node.addr,port:node.port}));
                    } catch (e) {
                        if (e.errno == "EINVAL") {
                            node.error(RED._("udp.errors.bad-mcaddress"));
                        } else if (e.errno == "ENODEV") {
                            node.error(RED._("udp.errors.interface"));
                        } else {
                            node.error(RED._("udp.errors.error",{error:e.errno}));
                        }
                    }
                } else {
                    node.log(RED._("udp.status.bc-ready",{outport:node.outport,host:node.addr,port:node.port}));
                }
            });
        } else if ((node.outport !== "") && (!udpInputPortsInUse[this.outport])) {
            sock.bind(node.outport);
            node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port}));
        } else {
            node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port}));
        }
 
        node.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                var add = node.addr || msg.ip || "";
                var por = node.port || msg.port || 0;
                if (add === "") {
                    node.warn(RED._("udp.errors.ip-notset"));
                } else if (por === 0) {
                    node.warn(RED._("udp.errors.port-notset"));
                } else if (isNaN(por) || (por < 1) || (por > 65535)) {
                    node.warn(RED._("udp.errors.port-invalid"));
                } else {
                    var message;
                    if (node.base64) {
                        message = new Buffer(msg.payload, 'base64');
                    } else if (msg.payload instanceof Buffer) {
                        message = msg.payload;
                    } else {
                        message = new Buffer(""+msg.payload);
                    }
                    sock.send(message, 0, message.length, por, add, function(err, bytes) {
                        if (err) {
                            node.error("udp : "+err,msg);
                        }
                        message = null;
                    });
                }
            }
        });
 
        node.on("close", function() {
            if (udpInputPortsInUse.hasOwnProperty(node.outport)) {
                delete udpInputPortsInUse[node.outport];
            }
            try {
                sock.close();
                node.log(RED._("udp.status.output-stopped"));
            } catch (err) {
                //node.error(err);
            }
        });
    }
    RED.nodes.registerType("udp out",UDPout);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/

Statements: 4.2% (18 / 429)      Branches: 0% (0 / 336)      Functions: 12.5% (4 / 32)      Lines: 4.29% (18 / 420)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/nodes/core/logic/
File Statements Branches Functions Lines
10-switch.js 4.9% (5 / 102) 0% (0 / 56) 5.88% (1 / 17) 5.05% (5 / 99)
15-change.js 3.11% (5 / 161) 0% (0 / 161) 25% (1 / 4) 3.11% (5 / 161)
16-range.js 9.68% (3 / 31) 0% (0 / 20) 33.33% (1 / 3) 11.54% (3 / 26)
17-split.js 3.7% (5 / 135) 0% (0 / 99) 12.5% (1 / 8) 3.73% (5 / 134)
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/10-switch.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/10-switch.js

Statements: 4.9% (5 / 102)      Branches: 0% (0 / 56)      Functions: 5.88% (1 / 17)      Lines: 5.05% (5 / 99)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162                                1     1   1                                 1                                                                                                                                                                                                                                               1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
 
    var jsonata = require('jsonata');
 
    var operators = {
        'eq': function(a, b) { return a == b; },
        'neq': function(a, b) { return a != b; },
        'lt': function(a, b) { return a < b; },
        'lte': function(a, b) { return a <= b; },
        'gt': function(a, b) { return a > b; },
        'gte': function(a, b) { return a >= b; },
        'btwn': function(a, b, c) { return a >= b && a <= c; },
        'cont': function(a, b) { return (a + "").indexOf(b) != -1; },
        'regex': function(a, b, c, d) { return (a + "").match(new RegExp(b,d?'i':'')); },
        'true': function(a) { return a === true; },
        'false': function(a) { return a === false; },
        'null': function(a) { return (typeof a == "undefined" || a === null); },
        'nnull': function(a) { return (typeof a != "undefined" && a !== null); },
        'else': function(a) { return a === true; }
    };
 
    function SwitchNode(n) {
        RED.nodes.createNode(this, n);
        this.rules = n.rules || [];
        this.property = n.property;
        this.propertyType = n.propertyType || "msg";
 
        if (this.propertyType === 'jsonata') {
            try {
                this.property = jsonata(this.property);
            } catch(err) {
                this.error(RED._("switch.errors.invalid-expr",{error:err.message}));
                return;
            }
        }
 
        this.checkall = n.checkall || "true";
        this.previousValue = null;
        var node = this;
        var valid = true;
        for (var i=0; i<this.rules.length; i+=1) {
            var rule = this.rules[i];
            if (!rule.vt) {
                if (!isNaN(Number(rule.v))) {
                    rule.vt = 'num';
                } else {
                    rule.vt = 'str';
                }
            }
            if (rule.vt === 'num') {
                if (!isNaN(Number(rule.v))) {
                    rule.v = Number(rule.v);
                }
            } else if (rule.vt === "jsonata") {
                try {
                    rule.v = jsonata(rule.v);
                } catch(err) {
                    this.error(RED._("switch.errors.invalid-expr",{error:err.message}));
                    valid = false;
                }
            }
            if (typeof rule.v2 !== 'undefined') {
                if (!rule.v2t) {
                    if (!isNaN(Number(rule.v2))) {
                        rule.v2t = 'num';
                    } else {
                        rule.v2t = 'str';
                    }
                }
                if (rule.v2t === 'num') {
                    rule.v2 = Number(rule.v2);
                } else if (rule.v2t === 'jsonata') {
                    try {
                        rule.v2 = jsonata(rule.v2);
                    } catch(err) {
                        this.error(RED._("switch.errors.invalid-expr",{error:err.message}));
                        valid = false;
                    }
                }
            }
        }
 
        if (!valid) {
            return;
        }
 
        this.on('input', function (msg) {
            var onward = [];
            try {
                var prop;
                if (node.propertyType === 'jsonata') {
                    prop = node.property.evaluate({msg:msg});
                } else {
                    prop = RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg);
                }
                var elseflag = true;
                for (var i=0; i<node.rules.length; i+=1) {
                    var rule = node.rules[i];
                    var test = prop;
                    var v1,v2;
                    if (rule.vt === 'prev') {
                        v1 = node.previousValue;
                    } else if (rule.vt === 'jsonata') {
                        try {
                            v1 = rule.v.evaluate({msg:msg});
                        } catch(err) {
                            node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
                            return;
                        }
                    } else {
                        v1 = RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
                    }
                    v2 = rule.v2;
                    if (rule.v2t === 'prev') {
                        v2 = node.previousValue;
                    } else if (rule.v2t === 'jsonata') {
                        try {
                            v2 = rule.v2.evaluate({msg:msg});
                        } catch(err) {
                            node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
                            return;
                        }
                    } else if (typeof v2 !== 'undefined') {
                        v2 = RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg);
                    }
                    if (rule.t == "else") { test = elseflag; elseflag = true; }
                    if (operators[rule.t](test,v1,v2,rule.case)) {
                        onward.push(msg);
                        elseflag = false;
                        if (node.checkall == "false") { break; }
                    } else {
                        onward.push(null);
                    }
                }
                node.previousValue = prop;
                this.send(onward);
            } catch(err) {
                node.warn(err);
            }
        });
    }
    RED.nodes.registerType("switch", SwitchNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/15-change.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/15-change.js

Statements: 3.11% (5 / 161)      Branches: 0% (0 / 161)      Functions: 25% (1 / 4)      Lines: 3.11% (5 / 161)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252                                1   1   1                                                                                                                                                           1                                                                                                                                                                                                                                                                                                           1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var jsonata = require("jsonata");
 
    function ChangeNode(n) {
        RED.nodes.createNode(this, n);
 
        this.rules = n.rules;
        var rule;
        if (!this.rules) {
            rule = {
                t:(n.action=="replace"?"set":n.action),
                p:n.property||""
            }
 
            if ((rule.t === "set")||(rule.t === "move")) {
                rule.to = n.to||"";
            } else if (rule.t === "change") {
                rule.from = n.from||"";
                rule.to = n.to||"";
                rule.re = (n.reg===null||n.reg);
            }
            this.rules = [rule];
        }
 
        var valid = true;
        for (var i=0;i<this.rules.length;i++) {
            rule = this.rules[i];
            // Migrate to type-aware rules
            if (!rule.pt) {
                rule.pt = "msg";
            }
            if (rule.t === "change" && rule.re) {
                rule.fromt = 're';
                delete rule.re;
            }
            if (rule.t === "set" && !rule.tot) {
                if (rule.to.indexOf("msg.") === 0 && !rule.tot) {
                    rule.to = rule.to.substring(4);
                    rule.tot = "msg";
                }
            }
            if (!rule.tot) {
                rule.tot = "str";
            }
            if (!rule.fromt) {
                rule.fromt = "str";
            }
            if (rule.t === "change" && rule.fromt !== 'msg' && rule.fromt !== 'flow' && rule.fromt !== 'global') {
                rule.fromRE = rule.from;
                if (rule.fromt !== 're') {
                    rule.fromRE = rule.fromRE.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
                }
                try {
                    rule.fromRE = new RegExp(rule.fromRE, "g");
                } catch (e) {
                    valid = false;
                    this.error(RED._("change.errors.invalid-from",{error:e.message}));
                }
            }
            if (rule.tot === 'num') {
                rule.to = Number(rule.to);
            } else if (rule.tot === 'json') {
                try {
                    // check this is parsable JSON
                    JSON.parse(rule.to);
                } catch(e2) {
                    valid = false;
                    this.error(RED._("change.errors.invalid-json"));
                }
            } else if (rule.tot === 'bool') {
                rule.to = /^true$/i.test(rule.to);
            } else if (rule.tot === 'jsonata') {
                try {
                    rule.to = jsonata(rule.to);
                } catch(e) {
                    valid = false;
                    this.error(RED._("change.errors.invalid-from",{error:e.message}));
                }
            }
        }
 
        function applyRule(msg,rule) {
            try {
                var property = rule.p;
                var value = rule.to;
                if (rule.tot === 'json') {
                    value = JSON.parse(rule.to);
                }
                var current;
                var fromValue;
                var fromType;
                var fromRE;
                if (rule.tot === "msg") {
                    value = RED.util.getMessageProperty(msg,rule.to);
                } else if (rule.tot === 'flow') {
                    value = node.context().flow.get(rule.to);
                } else if (rule.tot === 'global') {
                    value = node.context().global.get(rule.to);
                } else if (rule.tot === 'date') {
                    value = Date.now();
                } else if (rule.tot === 'jsonata') {
                    value = rule.to.evaluate({msg:msg});
                }
                if (rule.t === 'change') {
                    if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') {
                        if (rule.fromt === "msg") {
                            fromValue = RED.util.getMessageProperty(msg,rule.from);
                        } else if (rule.tot === 'flow') {
                            fromValue = node.context().flow.get(rule.from);
                        } else if (rule.tot === 'global') {
                            fromValue = node.context().global.get(rule.from);
                        }
                        if (typeof fromValue === 'number' || fromValue instanceof Number) {
                            fromType = 'num';
                        } else if (typeof fromValue === 'boolean') {
                            fromType = 'bool'
                        } else if (fromValue instanceof RegExp) {
                            fromType = 're';
                            fromRE = fromValue;
                        } else if (typeof fromValue === 'string') {
                            fromType = 'str';
                            fromRE = fromValue.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
                            try {
                                fromRE = new RegExp(fromRE, "g");
                            } catch (e) {
                                valid = false;
                                node.error(RED._("change.errors.invalid-from",{error:e.message}));
                                return;
                            }
                        } else {
                            node.error(RED._("change.errors.invalid-from",{error:"unsupported type: "+(typeof fromValue)}));
                            return
                        }
                    } else {
                        fromType = rule.fromt;
                        fromValue = rule.from;
                        fromRE = rule.fromRE;
                    }
                }
                if (rule.pt === 'msg') {
                    if (rule.t === 'delete') {
                        RED.util.setMessageProperty(msg,property,undefined);
                    } else if (rule.t === 'set') {
                        RED.util.setMessageProperty(msg,property,value);
                    } else if (rule.t === 'change') {
                        current = RED.util.getMessageProperty(msg,property);
                        if (typeof current === 'string') {
                            if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) {
                                // str representation of exact from number/boolean
                                // only replace if they match exactly
                                RED.util.setMessageProperty(msg,property,value);
                            } else {
                                current = current.replace(fromRE,value);
                                RED.util.setMessageProperty(msg,property,current);
                            }
                        } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
                            if (current == Number(fromValue)) {
                                RED.util.setMessageProperty(msg,property,value);
                            }
                        } else if (typeof current === 'boolean' && fromType === 'bool') {
                            if (current.toString() === fromValue) {
                                RED.util.setMessageProperty(msg,property,value);
                            }
                        }
                    }
                }
                else {
                    var target;
                    if (rule.pt === 'flow') {
                        target = node.context().flow;
                    } else if (rule.pt === 'global') {
                        target = node.context().global;
                    }
                    if (target) {
                        if (rule.t === 'delete') {
                            target.set(property,undefined);
                        } else if (rule.t === 'set') {
                            target.set(property,value);
                        } else if (rule.t === 'change') {
                            current = target.get(msg,property);
                            if (typeof current === 'string') {
                                if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) {
                                    // str representation of exact from number/boolean
                                    // only replace if they match exactly
                                    target.set(property,value);
                                } else {
                                    current = current.replace(fromRE,value);
                                    target.set(property,current);
                                }
                            } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
                                if (current == Number(fromValue)) {
                                    target.set(property,value);
                                }
                            } else if (typeof current === 'boolean' && fromType === 'bool') {
                                if (current.toString() === fromValue) {
                                    target.set(property,value);
                                }
                            }
                        }
                    }
                }
            } catch(err) {/*console.log(err.stack)*/}
            return msg;
        }
        if (valid) {
            var node = this;
            this.on('input', function(msg) {
                for (var i=0; i<this.rules.length; i++) {
                    if (this.rules[i].t === "move") {
                        var r = this.rules[i];
                        if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) {
                            msg = applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt});
                            applyRule(msg,{t:"delete", p:r.p, pt:r.pt});
                        }
                        else { // 2 step move if we are moving from a child
                            msg = applyRule(msg,{t:"set", p:"_temp_move", pt:r.tot, to:r.p, tot:r.pt});
                            applyRule(msg,{t:"delete", p:r.p, pt:r.pt});
                            msg = applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:"_temp_move", tot:r.pt});
                            applyRule(msg,{t:"delete", p:"_temp_move", pt:r.pt});
                        }
                    } else {
                        msg = applyRule(msg,this.rules[i]);
                    }
                    if (msg === null) {
                        return;
                    }
                }
                node.send(msg);
            });
        }
    }
    RED.nodes.registerType("change", ChangeNode);
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/16-range.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/16-range.js

Statements: 9.68% (3 / 31)      Branches: 0% (0 / 20)      Functions: 33.33% (1 / 3)      Lines: 11.54% (3 / 26)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53                                1   1                                                             1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    function RangeNode(n) {
        RED.nodes.createNode(this, n);
        this.action = n.action;
        this.round = n.round || false;
        this.minin = Number(n.minin);
        this.maxin = Number(n.maxin);
        this.minout = Number(n.minout);
        this.maxout = Number(n.maxout);
        var node = this;
 
        this.on('input', function (msg) {
            if (msg.hasOwnProperty("payload")) {
                var n = Number(msg.payload);
                if (!isNaN(n)) {
                    if (node.action == "clamp") {
                        if (n < node.minin) { n = node.minin; }
                        if (n > node.maxin) { n = node.maxin; }
                    }
                    if (node.action == "roll") {
                        if (n >= node.maxin) { n = (n - node.minin) % (node.maxin - node.minin) + node.minin; }
                        if (n <  node.minin) { n = (n - node.minin) % (node.maxin - node.minin) + node.maxin; }
                    }
                    msg.payload = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout;
                    if (node.round) { msg.payload = Math.round(msg.payload); }
                    node.send(msg);
                }
                else { node.log(RED._("range.errors.notnumber")+": "+msg.payload); }
            }
            else { node.send(msg); } // If no payload - just pass it on.
        });
    }
    RED.nodes.registerType("range", RangeNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/17-split.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/logic/17-split.js

Statements: 3.7% (5 / 135)      Branches: 0% (0 / 99)      Functions: 12.5% (1 / 8)      Lines: 3.73% (5 / 134)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230                                1     1                                                                                         1     1                                                                                                                                                                                                                                                                                                                             1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
 
    function SplitNode(n) {
        RED.nodes.createNode(this,n);
        this.splt = (n.splt || "\\n").replace(/\\n/,"\n").replace(/\\r/,"\r").replace(/\\t/,"\t").replace(/\\e/,"\e").replace(/\\f/,"\f").replace(/\\0/,"\0");
        var node = this;
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                var a = msg.payload;
                if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack
                else { msg.parts = {}; }
                msg.parts.id = msg._msgid;  // use the existing _msgid by default.
                if (typeof msg.payload === "string") { // Split String into array
                    a = msg.payload.split(node.splt);
                    msg.parts.ch = node.splt; // pass the split char to other end for rejoin
                    msg.parts.type = "string";
                }
                if (Array.isArray(a)) { // then split array into messages
                    msg.parts.type = msg.parts.type || "array";  // if it wasn't a string in the first place
                    for (var i = 0; i < a.length; i++) {
                        msg.payload = a[i];
                        msg.parts.index = i;
                        msg.parts.count = a.length;
                        node.send(RED.util.cloneMessage(msg));
                    }
                }
                else if ((typeof msg.payload === "object") && !Buffer.isBuffer(msg.payload)) {
                    var j = 0;
                    var l = Object.keys(msg.payload).length;
                    var pay = msg.payload;
                    msg.parts.type = "object";
                    for (var p in pay) {
                        if (pay.hasOwnProperty(p)) {
                            msg.payload = pay[p];
                            msg.parts.key = p;
                            msg.parts.index = j;
                            msg.parts.count = l;
                            node.send(RED.util.cloneMessage(msg));
                            j += 1;
                        }
                    }
                }
                // TODO not handling Buffers at present...
                //else {  }   // otherwise drop the message.
            }
        });
    }
    RED.nodes.registerType("split",SplitNode);
 
 
    function JoinNode(n) {
        RED.nodes.createNode(this,n);
        this.mode = n.mode||"auto";
        this.property = n.property||"payload";
        this.propertyType = n.propertyType||"msg";
        if (this.propertyType === 'full') {
            this.property = "payload";
        }
        this.key = n.key||"topic";
        this.timer = (this.mode === "auto") ? 0 : Number(n.timeout || 0)*1000;
        this.timerr = n.timerr || "send";
        this.count = Number(n.count || 0);
        this.joiner = (n.joiner||"").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0");
        this.build = n.build || "array";
        var node = this;
        var inflight = {};
 
        var completeSend = function(partId) {
            var group = inflight[partId];
            clearTimeout(group.timeout);
            delete inflight[partId];
 
            if (group.type === 'string') {
                RED.util.setMessageProperty(group.msg,node.property,group.payload.join(group.joinChar));
            } else {
                RED.util.setMessageProperty(group.msg,node.property,group.payload);
            }
            if (group.msg.hasOwnProperty('parts') && group.msg.parts.hasOwnProperty('parts')) {
                group.msg.parts = group.msg.parts.parts;
            } else {
                delete group.msg.parts;
            }
            node.send(group.msg);
        }
 
        this.on("input", function(msg) {
try {
            var property;
            if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) {
                node.warn("Message missing msg.parts property - cannot join in 'auto' mode")
                return;
            }
            if (node.propertyType == "full") {
                property = msg;
            } else {
                try {
                    property = RED.util.getMessageProperty(msg,node.property);
                } catch(err) {
                    node.warn("Message property "+node.property+" not found");
                    return;
                }
            }
 
            var partId;
            var payloadType;
            var propertyKey;
            var targetCount;
            var joinChar;
            var propertyIndex;
            if (node.mode === "auto") {
                // Use msg.parts to identify all of the group information
                partId = msg.parts.id;
                payloadType = msg.parts.type;
                targetCount = msg.parts.count;
                joinChar = msg.parts.ch;
                propertyKey = msg.parts.key;
                propertyIndex = msg.parts.index;
            } else {
                // Use the node configuration to identify all of the group information
                partId = "_";
                payloadType = node.build;
                targetCount = node.count;
                joinChar = node.joiner;
                if (targetCount === 0 && msg.hasOwnProperty('parts')) {
                    targetCount = msg.parts.count || 0;
                }
                if (node.build === 'object') {
                    propertyKey = RED.util.getMessageProperty(msg,node.key);
                }
            }
            if (payloadType === 'object' && (propertyKey === null || propertyKey === undefined || propertyKey === "")) {
                if (node.mode === "auto") {
                    node.warn("Message missing 'msg.parts.key' property - cannot add to object");
                } else {
                    node.warn("Message missing key property 'msg."+node.key+"' '- cannot add to object")
                }
                return;
            }
            if (!inflight.hasOwnProperty(partId)) {
                if (payloadType === 'object' || payloadType === 'merged') {
                    inflight[partId] = {
                        currentCount:0,
                        payload:{},
                        targetCount:targetCount,
                        type:"object",
                        msg:msg
                    };
                } else {
                    inflight[partId] = {
                        currentCount:0,
                        payload:[],
                        targetCount:targetCount,
                        type:payloadType,
                        joinChar: joinChar,
                        msg:msg
                    };
                    if (payloadType === 'string') {
                        inflight[partId].joinChar = joinChar;
                    }
                }
                if (node.timer > 0) {
                    inflight[partId].timeout = setTimeout(function() {
                        completeSend(partId)
                    }, node.timer)
                }
            }
 
            var group = inflight[partId];
            if (payloadType === 'object') {
                group.payload[propertyKey] = property;
                group.currentCount = Object.keys(group.payload).length;
            } else if (payloadType === 'merged') {
                if (Array.isArray(property) || typeof property !== 'object') {
                    node.warn("Cannot merge non-object types");
                } else {
                    for (propertyKey in property) {
                        if (property.hasOwnProperty(propertyKey)) {
                            group.payload[propertyKey] = property[propertyKey];
                        }
                    }
                    group.currentCount++;
                }
            } else {
                if (!isNaN(propertyIndex)) {
                    group.payload[propertyIndex] = property;
                } else {
                    group.payload.push(property);
                }
                group.currentCount++;
            }
            // TODO: currently reuse the last received - add option to pick first received
            group.msg = msg;
            if (group.currentCount === group.targetCount || msg.hasOwnProperty('complete')) {
                delete msg.complete;
                completeSend(partId);
            }
} catch(err) {
    console.log(err.stack);
}
        });
 
        this.on("close", function() {
            for (var i in inflight) {
                if (inflight.hasOwnProperty(i)) {
                    clearTimeout(inflight[i].timeout);
                }
            }
        });
    }
    RED.nodes.registerType("join",JoinNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/

Statements: 7.54% (19 / 252)      Branches: 0.54% (1 / 185)      Functions: 27.78% (5 / 18)      Lines: 8.3% (19 / 229)      Ignored: 1 branch     

All files » node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/
File Statements Branches Functions Lines
70-CSV.js 2.24% (3 / 134) 0% (0 / 115) 25% (1 / 4) 2.48% (3 / 121)
70-HTML.js 9.76% (4 / 41) 3.13% (1 / 32) 25% (1 / 4) 11.76% (4 / 34)
70-JSON.js 14.29% (3 / 21) 0% (0 / 8) 33.33% (1 / 3) 14.29% (3 / 21)
70-XML.js 14.71% (5 / 34) 0% (0 / 22) 25% (1 / 4) 16.13% (5 / 31)
70-YAML.js 18.18% (4 / 22) 0% (0 / 8) 33.33% (1 / 3) 18.18% (4 / 22)
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-CSV.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-CSV.js

Statements: 2.24% (3 / 134)      Branches: 0% (0 / 115)      Functions: 25% (1 / 4)      Lines: 2.48% (3 / 121)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201                                1   1                                                                                                                                                                                                                                                                                                                                                                     1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    function CSVNode(n) {
        RED.nodes.createNode(this,n);
        this.template = (n.temp || "").split(",");
        this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r");
        this.quo = '"';
        this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r");
        this.winflag = (this.ret === "\r\n");
        this.lineend = "\n";
        this.multi = n.multi || "one";
        this.hdrin = n.hdrin || false;
        this.hdrout = n.hdrout || false;
        this.goodtmpl = true;
        var node = this;
 
        // pass in an array of column names to be trimed, de-quoted and retrimed
        var clean = function(col) {
            for (var t = 0; t < col.length; t++) {
                col[t] = col[t].trim(); // remove leading and trailing whitespace
                if (col[t].charAt(0) === '"' && col[t].charAt(col[t].length -1) === '"') {
                    // remove leading and trailing quotes (if they exist) - and remove whitepace again.
                    col[t] = col[t].substr(1,col[t].length -2).trim();
                }
            }
            if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
            else { node.goodtmpl = true; }
            return col;
        }
        node.template = clean(node.template);
 
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                if (typeof msg.payload == "object") { // convert object to CSV string
                    try {
                        var ou = "";
                        if (node.hdrout) {
                            ou += node.template.join(node.sep) + node.ret;
                        }
                        if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
                        for (var s = 0; s < msg.payload.length; s++) {
                            if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
                                if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
                                for (var t = 0; t < msg.payload[s].length; t++) {
                                    if (!msg.payload[s][t] && (msg.payload[s][t] !== 0)) { msg.payload[s][t] = ""; }
                                    if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
                                        msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""');
                                        msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
                                    }
                                    else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
                                        msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
                                    }
                                }
                                ou += msg.payload[s].join(node.sep) + node.ret;
                            }
                            else {
                                if ((node.template.length === 1) && (node.template[0] === '')) {
                                    node.warn(RED._("csv.errors.obj_csv"));
                                }
                                else {
                                    for (var t=0; t < node.template.length; t++) {
                                        if (node.template[t] === '') {
                                            ou += node.sep;
                                        }
                                        else {
                                            // aaargh - resorting to eval here - but fairly contained front and back.
                                            var p = RED.util.ensureString(eval("msg.payload[s]."+node.template[t]));
 
                                            if (p === "undefined") { p = ""; }
                                            if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
                                                p = p.replace(/"/g, '""');
                                                ou += node.quo + p + node.quo + node.sep;
                                            }
                                            else if (p.indexOf(node.sep) !== -1) { // add quotes if any "commas"
                                                ou += node.quo + p + node.quo + node.sep;
                                            }
                                            else { ou += p + node.sep; } // otherwise just add
                                        }
                                    }
                                    ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
                                }
                            }
                        }
                        msg.payload = ou;
                        if (msg.payload !== '') { node.send(msg); }
                    }
                    catch(e) { node.error(e,msg); }
                }
                else if (typeof msg.payload == "string") { // convert CSV string to object
                    try {
                        var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
                        var j = 0; // pointer into array of template items
                        var k = [""]; // array of data for each of the template items
                        var o = {}; // output object to build up
                        var a = []; // output array is needed for multiline option
                        var first = true; // is this the first line
                        var line = msg.payload;
                        var tmp = "";
                        var reg = new RegExp("^[-]?[0-9.]*[\.]?[0-9]*$");
 
                        // For now we are just going to assume that any \r or \n means an end of line...
                        //   got to be a weird csv that has singleton \r \n in it for another reason...
 
                        // Now process the whole file/line
                        for (var i = 0; i < line.length; i++) {
                            if ((node.hdrin === true) && first) { // if the template is in the first line
                                if ((line[i] === "\n")||(line[i] === "\r")) { // look for first line break
                                    node.template = clean(tmp.split(node.sep));
                                    first = false;
                                }
                                else { tmp += line[i]; }
                            }
                            else {
                                if (line[i] === node.quo) { // if it's a quote toggle inside or outside
                                    f = !f;
                                    if (line[i-1] === node.quo) { k[j] += '\"'; } // if it's a quotequote then it's actually a quote
                                    //if ((line[i-1] !== node.sep) && (line[i+1] !== node.sep)) { k[j] += line[i]; }
                                }
                                else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
                                    if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
                                    if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "" ) ) {
                                        if ( reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
                                        o[node.template[j]] = k[j];
                                    }
                                    j += 1;
                                    k[j] = "";
                                }
                                else if ((line[i] === "\n") || (line[i] === "\r")) { // handle multiple lines
                                    //console.log(j,k,o,k[j]);
                                    if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
                                    if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
                                        if ( reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
                                        else { k[j].replace(/\r$/,''); }
                                        o[node.template[j]] = k[j];
                                    }
                                    if (JSON.stringify(o) !== "{}") { // don't send empty objects
                                        if (node.multi === "one") {
                                            var newMessage = RED.util.cloneMessage(msg);
                                            newMessage.payload = o;
                                            node.send(newMessage); // either send
                                        }
                                        else { a.push(o); } // or add to the array
                                    }
                                    j = 0;
                                    k = [""];
                                    o = {};
                                    f = true; // reset in/out flag ready for next line.
                                }
                                else { // just add to the part of the message
                                    k[j] += line[i];
                                }
                            }
                        }
                        // Finished so finalize and send anything left
                        //console.log(j,k,o,k[j]);
                        if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
                        if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
                            if ( reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
                            else { k[j].replace(/\r$/,''); }
                            o[node.template[j]] = k[j];
                        }
                        if (JSON.stringify(o) !== "{}") { // don't send empty objects
                            if (node.multi === "one") {
                                var newMessage = RED.util.cloneMessage(msg);
                                newMessage.payload = o;
                                node.send(newMessage); // either send
                            }
                            else { a.push(o); } // or add to the aray
                        }
                        if (node.multi !== "one") {
                            msg.payload = a;
                            node.send(msg); // finally send the array
                        }
                    }
                    catch(e) { node.error(e,msg); }
                }
                else { node.warn(RED._("csv.errors.csv_js")); }
            }
            else { node.send(msg); } // If no payload - just pass it on.
        });
    }
    RED.nodes.registerType("csv",CSVNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-HTML.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-HTML.js

Statements: 9.76% (4 / 41)      Branches: 3.13% (1 / 32)      Functions: 25% (1 / 4)      Lines: 11.76% (4 / 34)      Ignored: 1 branch     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68                                1   1   1                                                                                       1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var cheerio = require('cheerio');
 
    function CheerioNode(n) {
        RED.nodes.createNode(this,n);
        this.tag = n.tag;
        this.ret = n.ret || "html";
        this.as = n.as || "single";
        var node = this;
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                var tag = node.tag;
                if (msg.hasOwnProperty("select")) { tag = node.tag || msg.select; }
                try {
                    var $ = cheerio.load(msg.payload);
                    var pay = [];
                    $(tag).each(function() {
                        if (node.as === "multi") {
                            var pay2 = null;
                            if (node.ret === "html") { pay2 = cheerio.load($(this).html().trim()).xml(); }
                            if (node.ret === "text") { pay2 = $(this).text(); }
                            if (node.ret === "attr") { pay2 = this.attribs; }
                            //if (node.ret === "val")  { pay2 = $(this).val(); }
                            /* istanbul ignore else */
                            if (pay2) {
                                msg.payload = pay2;
                                node.send(msg);
                            }
                        }
                        if (node.as === "single") {
                            if (node.ret === "html") { pay.push( cheerio.load($(this).html().trim()).xml() ); }
                            if (node.ret === "text") { pay.push( $(this).text() ); }
                            if (node.ret === "attr") { pay.push( this.attribs ); }
                            //if (node.ret === "val")  { pay.push( $(this).val() ); }
                        }
                    });
                    if ((node.as === "single") && (pay.length !== 0)) {
                        msg.payload = pay;
                        node.send(msg);
                    }
                } catch (error) {
                    node.error(error.message,msg);
                }
            }
            else { node.send(msg); } // If no payload - just pass it on.
        });
    }
    RED.nodes.registerType("html",CheerioNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-JSON.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-JSON.js

Statements: 14.29% (3 / 21)      Branches: 0% (0 / 8)      Functions: 33.33% (1 / 3)      Lines: 14.29% (3 / 21)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50                                1     1                                                     1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
 
    function JSONNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                if (typeof msg.payload === "string") {
                    try {
                        msg.payload = JSON.parse(msg.payload);
                        node.send(msg);
                    }
                    catch(e) { node.error(e.message,msg); }
                }
                else if (typeof msg.payload === "object") {
                    if (!Buffer.isBuffer(msg.payload)) {
                        try {
                            msg.payload = JSON.stringify(msg.payload);
                            node.send(msg);
                        }
                        catch(e) { node.error(RED._("json.errors.dropped-error")); }
                    }
                    else { node.warn(RED._("json.errors.dropped-object")); }
                }
                else { node.warn(RED._("json.errors.dropped")); }
            }
            else { node.send(msg); } // If no payload - just pass it on.
        });
    }
    RED.nodes.registerType("json",JSONNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-XML.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-XML.js

Statements: 14.71% (5 / 34)      Branches: 0% (0 / 22)      Functions: 25% (1 / 4)      Lines: 16.13% (5 / 31)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60                                1   1 1   1                                                                     1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var xml2js = require('xml2js');
    var parseString = xml2js.parseString;
 
    function XMLNode(n) {
        RED.nodes.createNode(this,n);
        this.attrkey = n.attr;
        this.charkey = n.chr;
        var node = this;
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                var options;
                if (typeof msg.payload === "object") {
                    options = {renderOpts:{pretty:false}};
                    if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; }
                    options.async = false;
                    var builder = new xml2js.Builder(options);
                    msg.payload = builder.buildObject(msg.payload, options);
                    node.send(msg);
                }
                else if (typeof msg.payload == "string") {
                    options = {};
                    if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; }
                    options.async = true;
                    options.attrkey = node.attrkey || options.attrkey || '$';
                    options.charkey = node.charkey || options.charkey || '_';
                    parseString(msg.payload, options, function (err, result) {
                        if (err) { node.error(err, msg); }
                        else {
                            msg.payload = result;
                            node.send(msg);
                        }
                    });
                }
                else { node.warn(RED._("xml.errors.xml_js")); }
            }
            else { node.send(msg); } // If no payload - just pass it on.
        });
    }
    RED.nodes.registerType("xml",XMLNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-YAML.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/parsers/70-YAML.js

Statements: 18.18% (4 / 22)      Branches: 0% (0 / 8)      Functions: 33.33% (1 / 3)      Lines: 18.18% (4 / 22)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37  1   1 1                                                         1      
 
module.exports = function(RED) {
    "use strict";
    var yaml = require('js-yaml');
    function YAMLNode(n) {
        RED.nodes.createNode(this,n);
        var node = this;
        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                if (typeof msg.payload === "string") {
                    try {
                        msg.payload = yaml.load(msg.payload);
                        node.send(msg);
                    }
                    catch(e) { node.error(e.message,msg); }
                }
                else if (typeof msg.payload === "object") {
                    if (!Buffer.isBuffer(msg.payload)) {
                        try {
                            msg.payload = yaml.dump(msg.payload);
                            node.send(msg);
                        }
                        catch(e) {
                            node.error(RED._("yaml.errors.dropped-error"));
                        }
                    }
                    else { node.warn(RED._("yaml.errors.dropped-object")); }
                }
                else { node.warn(RED._("yaml.errors.dropped")); }
            }
            else { node.send(msg); } // If no payload - just pass it on.
        });
    }
    RED.nodes.registerType("yaml",YAMLNode);
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/storage/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/storage/

Statements: 11.02% (13 / 118)      Branches: 2.44% (2 / 82)      Functions: 10% (2 / 20)      Lines: 12.26% (13 / 106)      Ignored: 1 branch     

All files » node-npmtest-node-red/node_modules/node-red/nodes/core/storage/
File Statements Branches Functions Lines
28-tail.js 19.35% (6 / 31) 14.29% (2 / 14) 20% (1 / 5) 20% (6 / 30)
50-file.js 8.05% (7 / 87) 0% (0 / 68) 6.67% (1 / 15) 9.21% (7 / 76)
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/storage/28-tail.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/storage/28-tail.js

Statements: 19.35% (6 / 31)      Branches: 14.29% (2 / 14)      Functions: 20% (1 / 5)      Lines: 20% (6 / 30)      Ignored: 1 branch     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77                                1   1 1   1       1                                                                                               1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var spawn = require('child_process').spawn;
    var plat = require('os').platform();
 
    Iif (plat.match(/^win/)) {
        throw RED._("tail.errors.windowsnotsupport");
    }
 
    function TailNode(n) {
        RED.nodes.createNode(this,n);
 
        this.filename = n.filename;
        this.filetype = n.filetype || "text";
        this.split = n.split || false;
        var node = this;
 
        var err = "";
        // TODO: rewrite to use node-tail
        var tail = spawn("tail", ["-F", "-n", "0", this.filename]);
        tail.stdout.on("data", function (data) {
            var msg = { topic:node.filename };
            if (node.filetype === "text") {
                if (node.split) {
                    // TODO: allow customisation of the line break - as we do elsewhere
                    var strings = data.toString().split("\n");
                    for (var s in strings) {
                        //TODO: should we really filter blanks? Is that expected?
                        if (strings[s] !== "") {
                            node.send({
                                topic: node.filename,
                                payload: strings[s]
                            });
                        }
                    }
                }
                else {
                    msg.payload = data.toString();
                    node.send(msg);
                }
            }
            else {
                msg.payload = data;
                node.send(msg);
            }
        });
 
        tail.stderr.on("data", function(data) {
            node.error(data.toString());
        });
 
        this.on("close", function() {
            /* istanbul ignore else */
            if (tail) { tail.kill(); }
        });
    }
 
    RED.nodes.registerType("tail",TailNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/storage/50-file.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/nodes/core/storage/50-file.js

Statements: 8.05% (7 / 87)      Branches: 0% (0 / 68)      Functions: 6.67% (1 / 15)      Lines: 9.21% (7 / 76)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140                                1   1 1   1                                                                                                                                                       1     1                                                                       1      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
module.exports = function(RED) {
    "use strict";
    var fs = require("fs-extra");
    var os = require("os");
 
    function FileNode(n) {
        RED.nodes.createNode(this,n);
        this.filename = n.filename;
        this.appendNewline = n.appendNewline;
        this.overwriteFile = n.overwriteFile.toString();
        this.createDir = n.createDir || false;
        var node = this;
 
        this.on("input",function(msg) {
            var filename = node.filename || msg.filename || "";
            if (!node.filename) {
                node.status({fill:"grey",shape:"dot",text:filename});
            }
            if (filename === "") {
                node.warn(RED._("file.errors.nofilename"));
            } else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) {
                var data = msg.payload;
                if ((typeof data === "object") && (!Buffer.isBuffer(data))) {
                    data = JSON.stringify(data);
                }
                if (typeof data === "boolean") { data = data.toString(); }
                if (typeof data === "number") { data = data.toString(); }
                if ((this.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; }
                data = new Buffer(data);
                if (this.overwriteFile === "true") {
                    // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while
                    //fs.writeFile(filename, data, "binary", function (err) {
                    fs.writeFile(filename, data, {encoding:"binary"}, function (err) {
                        if (err) {
                            if ((err.code === "ENOENT") && node.createDir) {
                                fs.ensureFile(filename, function (err) {
                                    if (err) { node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); }
                                    else {
                                        fs.writeFile(filename, data, "binary", function (err) {
                                            if (err) { node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); }
                                        });
                                    }
                                });
                            }
                            else { node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); }
                        }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.wrotefile",{file:filename})); }
                    });
                }
                else if (this.overwriteFile === "delete") {
                    fs.unlink(filename, function (err) {
                        if (err) { node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg); }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.deletedfile",{file:filename})); }
                    });
                }
                else {
                    // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while longer
                    //fs.appendFile(filename, data, "binary", function (err) {
                    fs.appendFile(filename, data, {encoding:"binary"}, function (err) {
                        if (err) {
                            if ((err.code === "ENOENT") && node.createDir) {
                                fs.ensureFile(filename, function (err) {
                                    if (err) { node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); }
                                    else {
                                        fs.appendFile(filename, data, "binary", function (err) {
                                            if (err) { node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); }
                                        });
                                    }
                                });
                            }
                            else { node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); }
                        }
                        else if (RED.settings.verbose) { node.log(RED._("file.status.appendedfile",{file:filename})); }
                    });
                }
            }
        });
        this.on('close', function() {
            node.status({});
        });
    }
    RED.nodes.registerType("file",FileNode);
 
 
    function FileInNode(n) {
        RED.nodes.createNode(this,n);
 
        this.filename = n.filename;
        this.format = n.format;
        var node = this;
        var options = {};
        if (this.format) {
            options['encoding'] = this.format;
        }
        this.on("input",function(msg) {
            var filename = node.filename || msg.filename || "";
            if (!node.filename) {
                node.status({fill:"grey",shape:"dot",text:filename});
            }
            if (filename === "") {
                node.warn(RED._("file.errors.nofilename"));
            } else {
                msg.filename = filename;
                fs.readFile(filename,options,function(err,data) {
                    if (err) {
                        node.error(err,msg);
                        msg.error = err;
                        delete msg.payload;
                    } else {
                        msg.payload = data;
                        delete msg.error;
                    }
                    node.send(msg);
                });
            }
        });
        this.on('close', function() {
            node.status({});
        });
    }
    RED.nodes.registerType("file in",FileInNode);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/public/red/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/public/red/

Statements: 4.86% (7 / 144)      Branches: 0% (0 / 42)      Functions: 5% (1 / 20)      Lines: 4.86% (7 / 144)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/public/red/
File Statements Branches Functions Lines
main.js 4.86% (7 / 144) 0% (0 / 42) 5% (1 / 20) 4.86% (7 / 144)
Code coverage report for node-npmtest-node-red/node_modules/node-red/public/red/main.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/public/red/main.js

Statements: 4.86% (7 / 144)      Branches: 0% (0 / 42)      Functions: 5% (1 / 20)      Lines: 4.86% (7 / 144)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276                              2   1                                                           1                                   1                                                                                                                                                                                                             1                     1                                                                                                                                                                   2                            
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
(function() {
 
    function loadNodeList() {
        $.ajax({
            headers: {
                "Accept":"application/json"
            },
            cache: false,
            url: 'nodes',
            success: function(data) {
                RED.nodes.setNodeList(data);
 
                var nsCount = 0;
                for (var i=0;i<data.length;i++) {
                    var ns = data[i];
                    if (ns.module != "node-red") {
                        nsCount++;
                        RED.i18n.loadCatalog(ns.id, function() {
                            nsCount--;
                            if (nsCount === 0) {
                                loadNodes();
                            }
                        });
                    }
                }
                if (nsCount === 0) {
                    loadNodes();
                }
            }
        });
    }
 
    function loadNodes() {
        $.ajax({
            headers: {
                "Accept":"text/html"
            },
            cache: false,
            url: 'nodes',
            success: function(data) {
                $("body").append(data);
                $("body").i18n();
                $("#palette > .palette-spinner").hide();
                $(".palette-scroll").removeClass("hide");
                $("#palette-search").removeClass("hide");
                loadFlows();
            }
        });
    }
 
    function loadFlows() {
        $.ajax({
            headers: {
                "Accept":"application/json",
            },
            cache: false,
            url: 'flows',
            success: function(nodes) {
                var currentHash = window.location.hash;
                RED.nodes.version(nodes.rev);
                RED.nodes.import(nodes.flows);
                RED.nodes.dirty(false);
                RED.view.redraw(true);
                if (/^#flow\/.+$/.test(currentHash)) {
                    RED.workspaces.show(currentHash.substring(6));
                }
 
                var persistentNotifications = {};
                RED.comms.subscribe("notification/#",function(topic,msg) {
                    var parts = topic.split("/");
                    var notificationId = parts[1];
                    if (msg.text) {
                        var text = RED._(msg.text,{default:msg.text});
                        if (!persistentNotifications.hasOwnProperty(notificationId)) {
                            persistentNotifications[notificationId] = RED.notify(text,msg.type,msg.timeout === undefined,msg.timeout);
                        } else {
                            persistentNotifications[notificationId].update(text,msg.timeout);
                        }
                    } else if (persistentNotifications.hasOwnProperty(notificationId)) {
                        persistentNotifications[notificationId].close();
                        delete persistentNotifications[notificationId];
                    }
                });
                RED.comms.subscribe("status/#",function(topic,msg) {
                    var parts = topic.split("/");
                    var node = RED.nodes.node(parts[1]);
                    if (node) {
                        if (msg.hasOwnProperty("text")) {
                            msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
                        }
                        node.status = msg;
                        node.dirty = true;
                        RED.view.redraw();
                    }
                });
                RED.comms.subscribe("node/#",function(topic,msg) {
                    var i,m;
                    var typeList;
                    var info;
                    if (topic == "node/added") {
                        var addedTypes = [];
                        msg.forEach(function(m) {
                            var id = m.id;
                            RED.nodes.addNodeSet(m);
                            addedTypes = addedTypes.concat(m.types);
                            RED.i18n.loadCatalog(id, function() {
                                $.get('nodes/'+id, function(data) {
                                    $("body").append(data);
                                });
                            });
                        });
                        if (addedTypes.length) {
                            typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
                            RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
                        }
                    } else if (topic == "node/removed") {
                        for (i=0;i<msg.length;i++) {
                            m = msg[i];
                            info = RED.nodes.removeNodeSet(m.id);
                            if (info.added) {
                                typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
                                RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
                            }
                        }
                    } else if (topic == "node/enabled") {
                        if (msg.types) {
                            info = RED.nodes.getNodeSet(msg.id);
                            if (info.added) {
                                RED.nodes.enableNodeSet(msg.id);
                                typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
                                RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
                            } else {
                                $.get('nodes/'+msg.id, function(data) {
                                    $("body").append(data);
                                    typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
                                    RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
                                });
                            }
                        }
                    } else if (topic == "node/disabled") {
                        if (msg.types) {
                            RED.nodes.disableNodeSet(msg.id);
                            typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
                            RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
                        }
                    }
                    // Refresh flow library to ensure any examples are updated
                    RED.library.loadFlowLibrary();
                });
            }
        });
    }
 
    function showAbout() {
        $.get('red/about', function(data) {
            var aboutHeader = '<div style="text-align:center;">'+
                                '<img width="50px" src="red/images/node-red-icon.svg" />'+
                              '</div>';
 
            RED.sidebar.info.set(aboutHeader+marked(data));
            RED.sidebar.info.show();
        });
    }
 
    function loadEditor() {
        var menuOptions = [];
        menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
            {id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"},
            {id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"},
            {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:"core:toggle-status", selected: true},
            null,
            // {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[
            //     {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.text.bidi.setTextDirection("")}}},
            //     {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("ltr")}}},
            //     {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("rtl")}}},
            //     {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("auto")}}}
            // ]},
            // null,
            {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true}
        ]});
        menuOptions.push(null);
        menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[
            {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-import-dialog"},
            {id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]}
        ]});
        menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[
            {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:"core:show-export-dialog"},
            {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"}
        ]});
        menuOptions.push(null);
        menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:"core:search"});
        menuOptions.push(null);
        menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"});
        menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
            {id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"},
            {id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"},
            {id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"}
        ]});
        menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
            {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"},
            {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"},
        ]});
        menuOptions.push(null);
        if (RED.settings.theme('palette.editable') !== false) {
            RED.palette.editor.init();
            menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"});
            menuOptions.push(null);
        }
 
        menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"});
        menuOptions.push({id:"menu-item-show-tips",label:RED._("menu.label.showTips"),toggle:true,selected:true,onselect:"core:toggle-show-tips"});
        menuOptions.push({id:"menu-item-help",
            label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"),
            href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
        });
        menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
 
 
        RED.user.init();
        RED.library.init();
        RED.palette.init();
        RED.sidebar.init();
        RED.subflow.init();
        RED.workspaces.init();
        RED.clipboard.init();
        RED.search.init();
        RED.view.init();
        RED.editor.init();
        RED.keyboard.init();
        RED.diff.init();
 
        RED.menu.init({id:"btn-sidemenu",options: menuOptions});
 
        RED.deploy.init(RED.settings.theme("deployButton",null));
 
        RED.actions.add("core:show-about", showAbout);
 
        RED.comms.connect();
 
        $("#main-container").show();
        $(".header-toolbar").show();
 
 
        loadNodeList();
    }
 
    $(function() {
 
        if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
            document.title = document.title+" : "+window.location.hostname;
        }
 
        ace.require("ace/ext/language_tools");
 
        RED.i18n.init(function() {
            RED.settings.init(loadEditor);
        })
    });
})();
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/

Statements: 78% (39 / 50)      Branches: 50% (8 / 16)      Functions: 81.82% (9 / 11)      Lines: 77.55% (38 / 49)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/red/
File Statements Branches Functions Lines
red.js 78% (39 / 50) 50% (8 / 16) 81.82% (9 / 11) 77.55% (38 / 49)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/red.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/red.js

Statements: 78% (39 / 50)      Branches: 50% (8 / 16)      Functions: 81.82% (9 / 11)      Lines: 77.55% (38 / 49)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111                                1 1   1 1   1   1 1 1 1   1 1 1                 1 1 1 1               1   1         1 1 1     1 1     1 1 1 1         1 1 1 1     1 1 1                                         2 3 3 2      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var fs = require("fs");
var path = require('path');
 
var runtime = require("./runtime");
var api = require("./api");
 
process.env.NODE_RED_HOME = process.env.NODE_RED_HOME || path.resolve(__dirname+"/..");
 
var nodeApp = null;
var adminApp = null;
var server = null;
var apiEnabled = false;
 
function checkVersion(userSettings) {
    var semver = require('semver');
    Iif (!semver.satisfies(process.version,">=4.0.0")) {
        // TODO: in the future, make this a hard error.
        // var e = new Error("Unsupported version of node.js");
        // e.code = "unsupported_version";
        // throw e;
        userSettings.UNSUPPORTED_VERSION = process.version;
    }
}
 
function checkBuild() {
    var editorFile = path.resolve(path.join(__dirname,"..","public","red","red.min.js"));
    try {
        var stats = fs.statSync(editorFile);
    } catch(err) {
        var e = new Error("Node-RED not built");
        e.code = "not_built";
        throw e;
    }
}
 
module.exports = {
    init: function(httpServer,userSettings) {
        Iif (!userSettings) {
            userSettings = httpServer;
            httpServer = null;
        }
 
        Eif (!userSettings.SKIP_BUILD_CHECK) {
            checkVersion(userSettings);
            checkBuild();
        }
 
        Eif (!userSettings.coreNodesDir) {
            userSettings.coreNodesDir = path.resolve(path.join(__dirname,"..","nodes"));
        }
 
        Eif (userSettings.httpAdminRoot !== false) {
            runtime.init(userSettings,api);
            api.init(httpServer,runtime);
            apiEnabled = true;
        } else {
            runtime.init(userSettings);
            apiEnabled = false;
        }
        adminApp = runtime.adminApi.adminApp;
        nodeApp = runtime.nodeApp;
        server = runtime.adminApi.server;
        return;
    },
    start: function() {
        return runtime.start().then(function() {
            Eif (apiEnabled) {
                return api.start();
            }
        });
    },
    stop: function() {
        return runtime.stop().then(function() {
            if (apiEnabled) {
                return api.stop();
            }
        })
    },
    nodes: runtime.nodes,
    log: runtime.log,
    settings:runtime.settings,
    util: runtime.util,
    version: runtime.version,
 
    comms: api.comms,
    library: api.library,
    auth: api.auth,
 
    get app() { console.log("Deprecated use of RED.app - use RED.httpAdmin instead"); return runtime.app },
    get httpAdmin() { return adminApp },
    get httpNode() { return nodeApp },
    get server() { return server }
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/

Statements: 31.11% (247 / 794)      Branches: 6.71% (22 / 328)      Functions: 22.02% (24 / 109)      Lines: 31.11% (247 / 794)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/red/api/
File Statements Branches Functions Lines
comms.js 38.58% (49 / 127) 18.75% (9 / 48) 28.57% (6 / 21) 38.58% (49 / 127)
credentials.js 22.73% (5 / 22) 0% (0 / 10) 50% (1 / 2) 22.73% (5 / 22)
flow.js 16.28% (7 / 43) 0% (0 / 22) 10% (1 / 10) 16.28% (7 / 43)
flows.js 15.91% (7 / 44) 0% (0 / 24) 14.29% (1 / 7) 15.91% (7 / 44)
index.js 79.63% (86 / 108) 36.36% (8 / 22) 55.56% (5 / 9) 79.63% (86 / 108)
info.js 35% (7 / 20) 0% (0 / 14) 50% (1 / 2) 35% (7 / 20)
library.js 19.53% (25 / 128) 2.63% (1 / 38) 8% (2 / 25) 19.53% (25 / 128)
locales.js 41.94% (13 / 31) 8.33% (1 / 12) 50% (3 / 6) 41.94% (13 / 31)
nodes.js 8.33% (13 / 156) 0% (0 / 76) 6.25% (1 / 16) 8.33% (13 / 156)
theme.js 22.22% (18 / 81) 5.17% (3 / 58) 16.67% (1 / 6) 22.22% (18 / 81)
ui.js 50% (17 / 34) 0% (0 / 4) 40% (2 / 5) 50% (17 / 34)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/comms.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/comms.js

Statements: 38.58% (49 / 127)      Branches: 18.75% (9 / 48)      Functions: 28.57% (6 / 21)      Lines: 38.58% (49 / 127)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236                                1 1   1 1   1 1 1   1   1 1   1     1 1   1 1 1 1   1 1   1 1     1 1 1 1 1 1 1 1 1 1                   1                                                                                                                                                                   1 1     1   1                   1                     1 7 7 1   6   7 7           1                     1                 1               1                 1              
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var ws = require("ws");
var log;
 
var server;
var settings;
 
var wsServer;
var pendingConnections = [];
var activeConnections = [];
 
var retained = {};
 
var heartbeatTimer;
var lastSentTime;
 
function handleStatus(event) {
    publish("status/"+event.id,event.status,true);
}
function handleRuntimeEvent(event) {
    publish("notification/"+event.id,event,true);
}
function init(_server,runtime) {
    server = _server;
    settings = runtime.settings;
    log = runtime.log;
 
    runtime.events.removeListener("node-status",handleStatus);
    runtime.events.on("node-status",handleStatus);
 
    runtime.events.removeListener("runtime-event",handleRuntimeEvent);
    runtime.events.on("runtime-event",handleRuntimeEvent);
}
 
function start() {
    var Tokens = require("./auth/tokens");
    var Users = require("./auth/users");
    var Permissions = require("./auth/permissions");
    Eif (!settings.disableEditor) {
        Users.default().then(function(anonymousUser) {
            var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
            var path = settings.httpAdminRoot || "/";
            path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
            wsServer = new ws.Server({
                server:server,
                path:path,
                // Disable the deflate option due to this issue
                //  https://github.com/websockets/ws/pull/632
                // that is fixed in the 1.x release of the ws module
                // that we cannot currently pickup as it drops node 0.10 support
                perMessageDeflate: false
            });
 
            wsServer.on('connection',function(ws) {
                log.audit({event: "comms.open"});
                var pendingAuth = (settings.adminAuth != null);
                if (!pendingAuth) {
                    activeConnections.push(ws);
                } else {
                    pendingConnections.push(ws);
                }
                ws.on('close',function() {
                    log.audit({event: "comms.close",user:ws.user});
                    removeActiveConnection(ws);
                    removePendingConnection(ws);
                });
                ws.on('message', function(data,flags) {
                    var msg = null;
                    try {
                        msg = JSON.parse(data);
                    } catch(err) {
                        log.trace("comms received malformed message : "+err.toString());
                        return;
                    }
                    if (!pendingAuth) {
                        if (msg.subscribe) {
                            handleRemoteSubscription(ws,msg.subscribe);
                        }
                    } else {
                        var completeConnection = function(userScope,sendAck) {
                            try {
                                if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
                                    ws.send(JSON.stringify({auth:"fail"}));
                                    ws.close();
                                } else {
                                    pendingAuth = false;
                                    removePendingConnection(ws);
                                    activeConnections.push(ws);
                                    if (sendAck) {
                                        ws.send(JSON.stringify({auth:"ok"}));
                                    }
                                }
                            } catch(err) {
                                // Just in case the socket closes before we attempt
                                // to send anything.
                            }
                        }
                        if (msg.auth) {
                            Tokens.get(msg.auth).then(function(client) {
                                if (client) {
                                    Users.get(client.user).then(function(user) {
                                        if (user) {
                                            ws.user = user;
                                            log.audit({event: "comms.auth",user:ws.user});
                                            completeConnection(client.scope,true);
                                        } else {
                                            log.audit({event: "comms.auth.fail"});
                                            completeConnection(null,false);
                                        }
                                    });
                                } else {
                                    log.audit({event: "comms.auth.fail"});
                                    completeConnection(null,false);
                                }
                            });
                        } else {
                            if (anonymousUser) {
                                log.audit({event: "comms.auth",user:anonymousUser});
                                completeConnection(anonymousUser.permissions,false);
                            } else {
                                log.audit({event: "comms.auth.fail"});
                                completeConnection(null,false);
                            }
                            //TODO: duplicated code - pull non-auth message handling out
                            if (msg.subscribe) {
                                handleRemoteSubscription(ws,msg.subscribe);
                            }
                        }
                    }
                });
                ws.on('error', function(err) {
                    log.warn(log._("comms.error",{message:err.toString()}));
                });
            });
 
            wsServer.on('error', function(err) {
                log.warn(log._("comms.error-server",{message:err.toString()}));
            });
 
            lastSentTime = Date.now();
 
            heartbeatTimer = setInterval(function() {
                var now = Date.now();
                if (now-lastSentTime > webSocketKeepAliveTime) {
                    publish("hb",lastSentTime);
                }
            }, webSocketKeepAliveTime);
        });
    }
}
 
function stop() {
    if (heartbeatTimer) {
        clearInterval(heartbeatTimer);
        heartbeatTimer = null;
    }
    if (wsServer) {
        wsServer.close();
        wsServer = null;
    }
}
 
function publish(topic,data,retain) {
    Eif (server) {
        if (retain) {
            retained[topic] = data;
        } else {
            delete retained[topic];
        }
        lastSentTime = Date.now();
        activeConnections.forEach(function(conn) {
            publishTo(conn,topic,data);
        });
    }
}
 
function publishTo(ws,topic,data) {
    var msg = JSON.stringify({topic:topic,data:data});
    try {
        ws.send(msg);
    } catch(err) {
        removeActiveConnection(ws);
        removePendingConnection(ws);
        log.warn(log._("comms.error-send",{message:err.toString()}));
    }
}
 
function handleRemoteSubscription(ws,topic) {
    var re = new RegExp("^"+topic.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
    for (var t in retained) {
        if (re.test(t)) {
            publishTo(ws,t,retained[t]);
        }
    }
}
 
function removeActiveConnection(ws) {
    for (var i=0;i<activeConnections.length;i++) {
        if (activeConnections[i] === ws) {
            activeConnections.splice(i,1);
            break;
        }
    }
}
function removePendingConnection(ws) {
    for (var i=0;i<pendingConnections.length;i++) {
        if (pendingConnections[i] === ws) {
            pendingConnections.splice(i,1);
            break;
        }
    }
}
 
module.exports = {
    init:init,
    start:start,
    stop:stop,
    publish:publish
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/credentials.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/credentials.js

Statements: 22.73% (5 / 22)      Branches: 0% (0 / 10)      Functions: 50% (1 / 2)      Lines: 22.73% (5 / 22)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54                                1 1   1   1 1                                                              
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var log;
var api;
 
module.exports = {
    init: function(runtime) {
        log = runtime.log;
        api = runtime.nodes;
    },
    get: function (req, res) {
        // TODO: It should verify the given node id is of the type specified -
        //       but that would add a dependency from this module to the
        //       registry module that knows about node types.
        var nodeType = req.params.type;
        var nodeID = req.params.id;
        log.audit({event: "credentials.get",type:nodeType,id:nodeID},req);
        var credentials = api.getCredentials(nodeID);
        if (!credentials) {
            res.json({});
            return;
        }
        var definition = api.getCredentialDefinition(nodeType);
 
        var sendCredentials = {};
        for (var cred in definition) {
            if (definition.hasOwnProperty(cred)) {
                if (definition[cred].type == "password") {
                    var key = 'has_' + cred;
                    sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
                    continue;
                }
                sendCredentials[cred] = credentials[cred] || '';
            }
        }
        res.json(sendCredentials);
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/flow.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/flow.js

Statements: 16.28% (7 / 43)      Branches: 0% (0 / 22)      Functions: 10% (1 / 10)      Lines: 16.28% (7 / 43)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89                                1 1 1   1   1 1 1                                                                                                                                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var log;
var redNodes;
var settings;
 
module.exports = {
    init: function(runtime) {
        settings = runtime.settings;
        redNodes = runtime.nodes;
        log = runtime.log;
    },
    get: function(req,res) {
        var id = req.params.id;
        var flow = redNodes.getFlow(id);
        if (flow) {
            log.audit({event: "flow.get",id:id},req);
            res.json(flow);
        } else {
            log.audit({event: "flow.get",id:id,error:"not_found"},req);
            res.status(404).end();
        }
    },
    post: function(req,res) {
        var flow = req.body;
        redNodes.addFlow(flow).then(function(id) {
            log.audit({event: "flow.add",id:id},req);
            res.json({id:id});
        }).otherwise(function(err) {
            log.audit({event: "flow.add",error:err.code||"unexpected_error",message:err.toString()},req);
            res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
        })
 
    },
    put: function(req,res) {
        var id = req.params.id;
        var flow = req.body;
        try {
            redNodes.updateFlow(id,flow).then(function() {
                log.audit({event: "flow.update",id:id},req);
                res.json({id:id});
            }).otherwise(function(err) {
                log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()},req);
                res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
            })
        } catch(err) {
            if (err.code === 404) {
                log.audit({event: "flow.update",id:id,error:"not_found"},req);
                res.status(404).end();
            } else {
                log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()},req);
                res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
            }
        }
    },
    delete: function(req,res) {
        var id = req.params.id;
        try {
            redNodes.removeFlow(id).then(function() {
                log.audit({event: "flow.remove",id:id},req);
                res.status(204).end();
            })
        } catch(err) {
            if (err.code === 404) {
                log.audit({event: "flow.remove",id:id,error:"not_found"},req);
                res.status(404).end();
            } else {
                log.audit({event: "flow.remove",id:id,error:err.code||"unexpected_error",message:err.toString()},req);
                res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
            }
        }
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/flows.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/flows.js

Statements: 15.91% (7 / 44)      Branches: 0% (0 / 24)      Functions: 14.29% (1 / 7)      Lines: 15.91% (7 / 44)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85                                1 1 1   1   1 1 1                                                                                                                        
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var log;
var redNodes;
var settings;
 
module.exports = {
    init: function(runtime) {
        settings = runtime.settings;
        redNodes = runtime.nodes;
        log = runtime.log;
    },
    get: function(req,res) {
        var version = req.get("Node-RED-API-Version")||"v1";
        if (version === "v1") {
            log.audit({event: "flows.get",version:"v1"},req);
            res.json(redNodes.getFlows().flows);
        } else if (version === "v2") {
            log.audit({event: "flows.get",version:"v2"},req);
            res.json(redNodes.getFlows());
        } else {
            log.audit({event: "flows.get",version:version,error:"invalid_api_version"},req);
            res.status(400).json({code:"invalid_api_version", message:"Invalid API Version requested"});
        }
    },
    post: function(req,res) {
        var version = req.get("Node-RED-API-Version")||"v1";
        var flows = req.body;
        var deploymentType = req.get("Node-RED-Deployment-Type")||"full";
        log.audit({event: "flows.set",type:deploymentType,version:version},req);
        if (deploymentType === 'reload') {
            redNodes.loadFlows().then(function() {
                res.status(204).end();
            }).otherwise(function(err) {
                log.warn(log._("api.flows.error-reload",{message:err.message}));
                log.warn(err.stack);
                res.status(500).json({error:"unexpected_error", message:err.message});
            });
        } else {
            var flowConfig = flows;
            if (version === "v2") {
                flowConfig = flows.flows;
                if (flows.hasOwnProperty('rev')) {
                    var currentVersion = redNodes.getFlows().rev;
                    if (currentVersion !== flows.rev) {
                        //TODO: log warning
                        return res.status(409).json({code:"version_mismatch"});
                    }
                }
            } else if (version !== 'v1') {
                log.audit({event: "flows.set",version:version,error:"invalid_api_version"},req);
                res.status(400).json({code:"invalid_api_version", message:"Invalid API Version requested"});
            }
            redNodes.setFlows(flowConfig,deploymentType).then(function(flowId) {
                if (version === "v1") {
                    res.status(204).end();
                } else if (version === "v2") {
                    res.json({rev:flowId});
                } else {
                    // TODO: invalid version
                }
            }).otherwise(function(err) {
                log.warn(log._("api.flows.error-save",{message:err.message}));
                log.warn(err.stack);
                res.status(500).json({error:"unexpected_error", message:err.message});
            });
        }
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/index.js

Statements: 79.63% (86 / 108)      Branches: 36.36% (8 / 22)      Functions: 55.56% (5 / 9)      Lines: 79.63% (86 / 108)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194                                1 1 1 1 1 1 1   1 1 1 1 1 1 1 1 1 1   1 1   1 1 1 1 1   1                   1                 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1     1 1 1 1                   1 1 1 1     1 1   1 1 1   1   1                     1           1 1   1 1 1 1     1 1   1 1 1   1 1   1   1     1 1 1     1           1 1 1         1     1       1                         40 40      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var express = require("express");
var bodyParser = require("body-parser");
var util = require('util');
var path = require('path');
var passport = require('passport');
var when = require('when');
var cors = require('cors');
 
var ui = require("./ui");
var nodes = require("./nodes");
var flows = require("./flows");
var flow = require("./flow");
var library = require("./library");
var info = require("./info");
var theme = require("./theme");
var locales = require("./locales");
var credentials = require("./credentials");
var comms = require("./comms");
 
var auth = require("./auth");
var needsPermission = auth.needsPermission;
 
var i18n;
var log;
var adminApp;
var server;
var runtime;
 
var errorHandler = function(err,req,res,next) {
    if (err.message === "request entity too large") {
        log.error(err);
    } else {
        console.log(err.stack);
    }
    log.audit({event: "api.error",error:err.code||"unexpected_error",message:err.toString()},req);
    res.status(400).json({error:"unexpected_error", message:err.toString()});
};
 
var ensureRuntimeStarted = function(req,res,next) {
    if (!runtime.isStarted()) {
        log.error("Node-RED runtime not started");
        res.status(503).send("Not started");
    } else {
        next();
    }
}
 
function init(_server,_runtime) {
    server = _server;
    runtime = _runtime;
    var settings = runtime.settings;
    i18n = runtime.i18n;
    log = runtime.log;
    Eif (settings.httpAdminRoot !== false) {
        comms.init(server,runtime);
        adminApp = express();
        auth.init(runtime);
        credentials.init(runtime);
        flows.init(runtime);
        flow.init(runtime);
        info.init(runtime);
        library.init(adminApp,runtime);
        locales.init(runtime);
        nodes.init(runtime);
 
        // Editor
        Eif (!settings.disableEditor) {
            ui.init(runtime);
            var editorApp = express();
            Iif (settings.requireHttps === true) {
                editorApp.enable('trust proxy');
                editorApp.use(function (req, res, next) {
                    if (req.secure) {
                        next();
                    } else {
                        res.redirect('https://' + req.headers.host + req.originalUrl);
                    }
                });
            }
            editorApp.get("/",ensureRuntimeStarted,ui.ensureSlash,ui.editor);
            editorApp.get("/icons/:icon",ui.icon);
            theme.init(runtime);
            Iif (settings.editorTheme) {
                editorApp.use("/theme",theme.app());
            }
            editorApp.use("/",ui.editorResources);
            adminApp.use(editorApp);
        }
        var maxApiRequestSize = settings.apiMaxLength || '5mb';
        adminApp.use(bodyParser.json({limit:maxApiRequestSize}));
        adminApp.use(bodyParser.urlencoded({limit:maxApiRequestSize,extended:true}));
 
        adminApp.get("/auth/login",auth.login,errorHandler);
 
        Iif (settings.adminAuth) {
            //TODO: all passport references ought to be in ./auth
            adminApp.use(passport.initialize());
            adminApp.post("/auth/token",
                auth.ensureClientSecret,
                auth.authenticateClient,
                auth.getToken,
                auth.errorHandler
            );
            adminApp.post("/auth/revoke",needsPermission(""),auth.revoke,errorHandler);
        }
        Iif (settings.httpAdminCors) {
            var corsHandler = cors(settings.httpAdminCors);
            adminApp.use(corsHandler);
        }
 
        // Flows
        adminApp.get("/flows",needsPermission("flows.read"),flows.get,errorHandler);
        adminApp.post("/flows",needsPermission("flows.write"),flows.post,errorHandler);
 
        adminApp.get("/flow/:id",needsPermission("flows.read"),flow.get,errorHandler);
        adminApp.post("/flow",needsPermission("flows.write"),flow.post,errorHandler);
        adminApp.delete("/flow/:id",needsPermission("flows.write"),flow.delete,errorHandler);
        adminApp.put("/flow/:id",needsPermission("flows.write"),flow.put,errorHandler);
 
        // Nodes
        adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,errorHandler);
        adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,errorHandler);
 
        adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,errorHandler);
        adminApp.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.putModule,errorHandler);
        adminApp.delete(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.delete,errorHandler);
 
        adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.read"),nodes.getSet,errorHandler);
        adminApp.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.write"),nodes.putSet,errorHandler);
 
        adminApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get,errorHandler);
 
        adminApp.get(/locales\/(.+)\/?$/,locales.get,errorHandler);
 
        // Library
        adminApp.post(new RegExp("/library/flows\/(.*)"),needsPermission("library.write"),library.post,errorHandler);
        adminApp.get("/library/flows",needsPermission("library.read"),library.getAll,errorHandler);
        adminApp.get(new RegExp("/library/flows\/(.*)"),needsPermission("library.read"),library.get,errorHandler);
 
        // Settings
        adminApp.get("/settings",needsPermission("settings.read"),info.settings,errorHandler);
 
        // Error Handler
        //adminApp.use(errorHandler);
    }
}
function start() {
    var catalogPath = path.resolve(path.join(__dirname,"locales"));
    return i18n.registerMessageCatalogs([
        {namespace: "editor",   dir: catalogPath, file:"editor.json"},
        {namespace: "jsonata",  dir: catalogPath, file:"jsonata.json"},
        {namespace: "infotips", dir: catalogPath, file:"infotips.json"}
    ]).then(function(){
        comms.start();
    });
}
function stop() {
    comms.stop();
    return when.resolve();
}
module.exports = {
    init: init,
    start: start,
    stop: stop,
    library: {
        register: library.register
    },
    auth: {
        needsPermission: auth.needsPermission
    },
    comms: {
        publish: comms.publish
    },
    get adminApp() { return adminApp; },
    get server() { return server; }
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/info.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/info.js

Statements: 35% (7 / 20)      Branches: 0% (0 / 14)      Functions: 50% (1 / 2)      Lines: 35% (7 / 20)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56                              1 1 1 1   1   1 1                                                                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
var theme = require("./theme");
var util = require('util');
var runtime;
var settings;
 
module.exports = {
    init: function(_runtime) {
        runtime = _runtime;
        settings = runtime.settings;
    },
    settings: function(req,res) {
        var safeSettings = {
            httpNodeRoot: settings.httpNodeRoot||"/",
            version: settings.version,
            user: req.user
        }
 
        var themeSettings = theme.settings();
        if (themeSettings) {
            safeSettings.editorTheme = themeSettings;
        }
 
        if (util.isArray(settings.paletteCategories)) {
            safeSettings.paletteCategories = settings.paletteCategories;
        }
 
        if (settings.flowFilePretty) {
            safeSettings.flowFilePretty = settings.flowFilePretty;
        }
 
        if (!runtime.nodes.paletteEditorEnabled()) {
            safeSettings.editorTheme = safeSettings.editorTheme || {};
            safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {};
            safeSettings.editorTheme.palette.editable = false;
        }
 
        res.json(safeSettings);
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/library.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/library.js

Statements: 19.53% (25 / 128)      Branches: 2.63% (1 / 38)      Functions: 8% (2 / 25)      Lines: 19.53% (25 / 128)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220                              1 1 1   1 1 1 1   1 2 2                                                 2                                             1 1 1   1                                                                         1           1         1   1 1 1       1 1 1 1                                                                                                                                                                    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
var fs = require('fs');
var fspath = require('path');
var when = require('when');
 
var redApp = null;
var storage;
var log;
var needsPermission = require("./auth").needsPermission;
 
function createLibrary(type) {
    Eif (redApp) {
        redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),needsPermission("library.read"),function(req,res) {
            var path = req.params[1]||"";
            storage.getLibraryEntry(type,path).then(function(result) {
                log.audit({event: "library.get",type:type},req);
                if (typeof result === "string") {
                    res.writeHead(200, {'Content-Type': 'text/plain'});
                    res.write(result);
                    res.end();
                } else {
                    res.json(result);
                }
            }).otherwise(function(err) {
                if (err) {
                    log.warn(log._("api.library.error-load-entry",{path:path,message:err.toString()}));
                    if (err.code === 'forbidden') {
                        log.audit({event: "library.get",type:type,error:"forbidden"},req);
                        res.status(403).end();
                        return;
                    }
                }
                log.audit({event: "library.get",type:type,error:"not_found"},req);
                res.status(404).end();
            });
        });
 
        redApp.post(new RegExp("/library/"+type+"\/(.*)"),needsPermission("library.write"),function(req,res) {
            var path = req.params[0];
            var meta = req.body;
            var text = meta.text;
            delete meta.text;
 
            storage.saveLibraryEntry(type,path,meta,text).then(function() {
                log.audit({event: "library.set",type:type},req);
                res.status(204).end();
            }).otherwise(function(err) {
                log.warn(log._("api.library.error-save-entry",{path:path,message:err.toString()}));
                    if (err.code === 'forbidden') {
                    log.audit({event: "library.set",type:type,error:"forbidden"},req);
                    res.status(403).end();
                    return;
                }
                log.audit({event: "library.set",type:type,error:"unexpected_error",message:err.toString()},req);
                res.status(500).json({error:"unexpected_error", message:err.toString()});
            });
        });
    }
}
 
var exampleRoots = {};
var exampleFlows = {d:{}};
var exampleCount = 0;
 
function getFlowsFromPath(path) {
    return when.promise(function(resolve,reject) {
        var result = {};
        fs.readdir(path,function(err,files) {
            var promises = [];
            var validFiles = [];
            files.forEach(function(file) {
                var fullPath = fspath.join(path,file);
                var stats = fs.lstatSync(fullPath);
                if (stats.isDirectory()) {
                    validFiles.push(file);
                    promises.push(getFlowsFromPath(fullPath));
                } else if (/\.json$/.test(file)){
                    validFiles.push(file);
                    exampleCount++;
                    promises.push(when.resolve(file.split(".")[0]))
                }
            })
            var i=0;
            when.all(promises).then(function(results) {
                results.forEach(function(r) {
                    if (typeof r === 'string') {
                        result.f = result.f||[];
                        result.f.push(r);
                    } else {
                        result.d = result.d||{};
                        result.d[validFiles[i]] = r;
                    }
                    i++;
                })
 
                resolve(result);
            })
        });
    })
}
 
function addNodeExamplesDir(module) {
    exampleRoots[module.name] = module.path;
    getFlowsFromPath(module.path).then(function(result) {
        exampleFlows.d[module.name] = result;
    });
}
function removeNodeExamplesDir(module) {
    delete exampleRoots[module];
    delete exampleFlows.d[module];
}
 
module.exports = {
    init: function(app,runtime) {
        redApp = app;
        log = runtime.log;
        storage = runtime.storage;
        // TODO: this allows init to be called multiple times without
        //       registering multiple instances of the listener.
        //       It isn't.... ideal.
        runtime.events.removeListener("node-examples-dir",addNodeExamplesDir);
        runtime.events.on("node-examples-dir",addNodeExamplesDir);
        runtime.events.removeListener("node-module-uninstalled",removeNodeExamplesDir);
        runtime.events.on("node-module-uninstalled",removeNodeExamplesDir);
 
    },
    register: createLibrary,
 
    getAll: function(req,res) {
        storage.getAllFlows().then(function(flows) {
            log.audit({event: "library.get.all",type:"flow"},req);
            if (exampleCount > 0) {
                flows.d = flows.d||{};
                flows.d._examples_ = exampleFlows;
            }
            res.json(flows);
        });
    },
    get: function(req,res) {
        if (req.params[0].indexOf("_examples_/") === 0) {
            var m = /^_examples_\/([^\/]+)\/(.*)$/.exec(req.params[0]);
            if (m) {
                var module = m[1];
                var path = m[2]+".json";
                if (exampleRoots[module]) {
                    var fullPath = fspath.join(exampleRoots[module],path);
                    try {
                        fs.statSync(fullPath);
                        log.audit({event: "library.get",type:"flow",path:req.params[0]},req);
                        return res.sendFile(fullPath,{
                            headers:{
                                'Content-Type': 'application/json'
                            }
                        })
                    } catch(err) {
                        console.log(err);
                    }
                }
            }
            // IF we get here, we didn't find the file
            log.audit({event: "library.get",type:"flow",path:req.params[0],error:"not_found"},req);
            return res.status(404).end();
        } else {
            storage.getFlow(req.params[0]).then(function(data) {
                // data is already a JSON string
                log.audit({event: "library.get",type:"flow",path:req.params[0]},req);
                res.set('Content-Type', 'application/json');
                res.send(data);
            }).otherwise(function(err) {
                if (err) {
                    log.warn(log._("api.library.error-load-flow",{path:req.params[0],message:err.toString()}));
                    if (err.code === 'forbidden') {
                        log.audit({event: "library.get",type:"flow",path:req.params[0],error:"forbidden"},req);
                        res.status(403).end();
                        return;
                    }
                }
                log.audit({event: "library.get",type:"flow",path:req.params[0],error:"not_found"},req);
                res.status(404).end();
            });
        }
    },
    post: function(req,res) {
        // if (req.params[0].indexOf("_examples_/") === 0) {
        //     log.warn(log._("api.library.error-save-flow",{path:req.params[0],message:"forbidden"}));
        //     log.audit({event: "library.set",type:"flow",path:req.params[0],error:"forbidden"},req);
        //     return res.status(403).send({error:"unexpected_error", message:"forbidden"});
        // }
        var flow = JSON.stringify(req.body);
        storage.saveFlow(req.params[0],flow).then(function() {
            log.audit({event: "library.set",type:"flow",path:req.params[0]},req);
            res.status(204).end();
        }).otherwise(function(err) {
            log.warn(log._("api.library.error-save-flow",{path:req.params[0],message:err.toString()}));
            if (err.code === 'forbidden') {
                log.audit({event: "library.set",type:"flow",path:req.params[0],error:"forbidden"},req);
                res.status(403).end();
                return;
            }
            log.audit({event: "library.set",type:"flow",path:req.params[0],error:"unexpected_error",message:err.toString()},req);
            res.status(500).send({error:"unexpected_error", message:err.toString()});
        });
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/locales.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/locales.js

Statements: 41.94% (13 / 31)      Branches: 8.33% (1 / 12)      Functions: 50% (3 / 6)      Lines: 41.94% (13 / 31)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66                              1 1 1 1   1   1 1 1 1         1                               1   1 1                                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
var fs = require('fs');
var path = require('path');
var i18n;
var supportedLangs = [];
 
var apiLocalDir = path.resolve(path.join(__dirname,"locales"));
 
var initSupportedLangs = function() {
    fs.readdir(apiLocalDir, function(err,files) {
        Eif(!err) {
            supportedLangs = files;
        }
    });
}
 
function determineLangFromHeaders(acceptedLanguages){
    var lang = i18n.defaultLang;
    acceptedLanguages = acceptedLanguages || [];
    for (var i=0;i<acceptedLanguages.length;i++){
        if (supportedLangs.indexOf(acceptedLanguages[i]) !== -1){
            lang = acceptedLanguages[i];
            break;
        // check the language without the country code
        } else if (supportedLangs.indexOf(acceptedLanguages[i].split("-")[0]) !== -1) {
            lang = acceptedLanguages[i].split("-")[0];
            break;
        }
    }
    return lang;
}
 
module.exports = {
    init: function(runtime) {
        i18n = runtime.i18n;
        initSupportedLangs();
    },
    get: function(req,res) {
        var namespace = req.params[0];
        namespace = namespace.replace(/\.json$/,"");
        var lang = determineLangFromHeaders(req.acceptsLanguages() || []);
        var prevLang = i18n.i.lng();
        i18n.i.setLng(lang, function(){
            var catalog = i18n.catalog(namespace,lang);
            res.json(catalog||{});
        });
        i18n.i.setLng(prevLang);
    },
    determineLangFromHeaders: determineLangFromHeaders
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/nodes.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/nodes.js

Statements: 8.33% (13 / 156)      Branches: 0% (0 / 76)      Functions: 6.25% (1 / 16)      Lines: 8.33% (13 / 156)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251                                1 1 1 1 1 1 1   1   1 1 1 1                                                                                                                                                                                                                                                                                                                                                                                             1                                                            
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var comms = require("./comms");
var locales = require("./locales");
var redNodes;
var log;
var i18n;
var settings;
 
module.exports = {
    init: function(runtime) {
        redNodes = runtime.nodes;
        log = runtime.log;
        i18n = runtime.i18n;
        settings = runtime.settings;
    },
    getAll: function(req,res) {
        if (req.get("accept") == "application/json") {
            log.audit({event: "nodes.list.get"},req);
            res.json(redNodes.getNodeList());
        } else {
            var lang = locales.determineLangFromHeaders(req.acceptsLanguages());
            log.audit({event: "nodes.configs.get"},req);
            res.send(redNodes.getNodeConfigs(lang));
        }
    },
 
    post: function(req,res) {
        if (!settings.available()) {
            log.audit({event: "nodes.install",error:"settings_unavailable"},req);
            res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
            return;
        }
        var node = req.body;
        var promise;
        if (node.module) {
            var module = redNodes.getModuleInfo(node.module);
            if (module) {
                log.audit({event: "nodes.install",module:node.module,error:"module_already_loaded"},req);
                res.status(400).json({error:"module_already_loaded", message:"Module already loaded"});
                return;
            }
            promise = redNodes.installModule(node.module);
        } else {
            log.audit({event: "nodes.install",module:node.module,error:"invalid_request"},req);
            res.status(400).json({error:"invalid_request", message:"Invalid request"});
            return;
        }
        promise.then(function(info) {
            comms.publish("node/added",info.nodes,false);
            if (node.module) {
                log.audit({event: "nodes.install",module:node.module},req);
                res.json(info);
            }
        }).otherwise(function(err) {
            if (err.code === 404) {
                log.audit({event: "nodes.install",module:node.module,error:"not_found"},req);
                res.status(404).end();
            } else if (err.code) {
                log.audit({event: "nodes.install",module:node.module,error:err.code},req);
                res.status(400).json({error:err.code, message:err.message});
            } else {
                log.audit({event: "nodes.install",module:node.module,error:err.code||"unexpected_error",message:err.toString()},req);
                res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
            }
        });
    },
 
    delete: function(req,res) {
        if (!settings.available()) {
            log.audit({event: "nodes.remove",error:"settings_unavailable"},req);
            res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
            return;
        }
        var mod = req.params[0];
        try {
            var promise = null;
            var module = redNodes.getModuleInfo(mod);
            if (!module) {
                log.audit({event: "nodes.remove",module:mod,error:"not_found"},req);
                res.status(404).end();
                return;
            } else {
                promise = redNodes.uninstallModule(mod);
            }
 
            promise.then(function(list) {
                comms.publish("node/removed",list,false);
                log.audit({event: "nodes.remove",module:mod},req);
                res.status(204).end();
            }).otherwise(function(err) {
                log.audit({event: "nodes.remove",module:mod,error:err.code||"unexpected_error",message:err.toString()},req);
                res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
            });
        } catch(err) {
            log.audit({event: "nodes.remove",module:mod,error:err.code||"unexpected_error",message:err.toString()},req);
            res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
        }
    },
 
    getSet: function(req,res) {
        var id = req.params[0] + "/" + req.params[2];
        var result = null;
        if (req.get("accept") === "application/json") {
            result = redNodes.getNodeInfo(id);
            if (result) {
                log.audit({event: "nodes.info.get",id:id},req);
                delete result.loaded;
                res.send(result);
            } else {
                log.audit({event: "nodes.info.get",id:id,error:"not_found"},req);
                res.status(404).end();
            }
        } else {
            var lang = locales.determineLangFromHeaders(req.acceptsLanguages());
            result = redNodes.getNodeConfig(id,lang);
            if (result) {
                log.audit({event: "nodes.config.get",id:id},req);
                res.send(result);
            } else {
                log.audit({event: "nodes.config.get",id:id,error:"not_found"},req);
                res.status(404).end();
            }
        }
    },
 
    getModule: function(req,res) {
        var module = req.params[0];
        var result = redNodes.getModuleInfo(module);
        if (result) {
            log.audit({event: "nodes.module.get",module:module},req);
            res.json(result);
        } else {
            log.audit({event: "nodes.module.get",module:module,error:"not_found"},req);
            res.status(404).end();
        }
    },
 
    putSet: function(req,res) {
        if (!settings.available()) {
            log.audit({event: "nodes.info.set",error:"settings_unavailable"},req);
            res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
            return;
        }
        var body = req.body;
        if (!body.hasOwnProperty("enabled")) {
            log.audit({event: "nodes.info.set",error:"invalid_request"},req);
            res.status(400).json({error:"invalid_request", message:"Invalid request"});
            return;
        }
        var id = req.params[0] + "/" + req.params[2];
        try {
            var node = redNodes.getNodeInfo(id);
            var info;
            if (!node) {
                log.audit({event: "nodes.info.set",id:id,error:"not_found"},req);
                res.status(404).end();
            } else {
                delete node.loaded;
                putNode(node, body.enabled).then(function(result) {
                    log.audit({event: "nodes.info.set",id:id,enabled:body.enabled},req);
                    res.json(result);
                });
            }
        } catch(err) {
            log.audit({event: "nodes.info.set",id:id,enabled:body.enabled,error:err.code||"unexpected_error",message:err.toString()},req);
            res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
        }
    },
 
    putModule: function(req,res) {
        if (!settings.available()) {
            log.audit({event: "nodes.module.set",error:"settings_unavailable"},req);
            res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
            return;
        }
        var body = req.body;
        if (!body.hasOwnProperty("enabled")) {
            log.audit({event: "nodes.module.set",error:"invalid_request"},req);
            res.status(400).json({error:"invalid_request", message:"Invalid request"});
            return;
        }
        var mod = req.params[0];
        try {
            var module = redNodes.getModuleInfo(mod);
            if (!module) {
                log.audit({event: "nodes.module.set",module:mod,error:"not_found"},req);
                return res.status(404).end();
            }
 
            var nodes = module.nodes;
            var promises = [];
            for (var i = 0; i < nodes.length; ++i) {
                promises.push(putNode(nodes[i],body.enabled));
            }
            when.settle(promises).then(function() {
                res.json(redNodes.getModuleInfo(mod));
            });
        } catch(err) {
            log.audit({event: "nodes.module.set",module:mod,enabled:body.enabled,error:err.code||"unexpected_error",message:err.toString()},req);
            res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
        }
    }
};
 
function putNode(node, enabled) {
    var info;
    var promise;
    if (!node.err && node.enabled === enabled) {
        promise = when.resolve(node);
    } else {
        if (enabled) {
            promise = redNodes.enableNode(node.id);
        } else {
            promise = redNodes.disableNode(node.id);
        }
 
        return promise.then(function(info) {
            if (info.enabled === enabled && !info.err) {
                comms.publish("node/"+(enabled?"enabled":"disabled"),info,false);
                log.info(" "+log._("api.nodes."+(enabled?"enabled":"disabled")));
                for (var i=0;i<info.types.length;i++) {
                    log.info(" - "+info.types[i]);
                }
            } else if (enabled && info.err) {
            log.warn(log._("api.nodes.error-enable"));
                log.warn(" - "+info.name+" : "+info.err);
            }
            return info;
        });
    }
 
    return promise;
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/theme.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/theme.js

Statements: 22.22% (18 / 81)      Branches: 5.17% (3 / 58)      Functions: 16.67% (1 / 6)      Lines: 22.22% (18 / 81)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180                                1 1 1 1 1   1                                 1 1 1 1   1                             1   1 1 1 1   1 1                                                                                                                                                                                                                                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var express = require("express");
var util = require("util");
var path = require("path");
var fs = require("fs");
var clone = require("clone");
 
var defaultContext = {
    page: {
        title: "Node-RED",
        favicon: "favicon.ico",
        tabicon: "red/images/node-red-icon-black.svg"
    },
    header: {
        title: "Node-RED",
        image: "red/images/node-red.png"
    },
    asset: {
        red: (process.env.NODE_ENV == "development")? "red/red.js":"red/red.min.js",
        main: (process.env.NODE_ENV == "development")? "red/main.js":"red/main.min.js",
 
    }
};
 
var theme = null;
var themeContext = clone(defaultContext);
var themeSettings = null;
var runtime = null;
 
function serveFile(app,baseUrl,file) {
    try {
        var stats = fs.statSync(file);
        var url = baseUrl+path.basename(file);
        //console.log(url,"->",file);
        app.get(url,function(req, res) {
            res.sendFile(file);
        });
        return "theme"+url;
    } catch(err) {
        //TODO: log filenotfound
        return null;
    }
}
 
module.exports = {
    init: function(runtime) {
        var settings = runtime.settings;
        themeContext = clone(defaultContext);
        Eif (runtime.version) {
            themeContext.version = runtime.version();
        }
        themeSettings = null;
        theme = settings.editorTheme;
    },
 
    app: function() {
        var i;
        var url;
        themeSettings = {};
 
        var themeApp = express();
 
        if (theme.page) {
            if (theme.page.css) {
                var styles = theme.page.css;
                if (!util.isArray(styles)) {
                    styles = [styles];
                }
                themeContext.page.css = [];
 
                for (i=0;i<styles.length;i++) {
                    url = serveFile(themeApp,"/css/",styles[i]);
                    if (url) {
                        themeContext.page.css.push(url);
                    }
                }
            }
 
            if (theme.page.favicon) {
                url = serveFile(themeApp,"/favicon/",theme.page.favicon)
                if (url) {
                    themeContext.page.favicon = url;
                }
            }
 
            if (theme.page.tabicon) {
                url = serveFile(themeApp,"/tabicon/",theme.page.tabicon)
                if (url) {
                    themeContext.page.tabicon = url;
                }
            }
 
            themeContext.page.title = theme.page.title || themeContext.page.title;
        }
 
        if (theme.header) {
 
            themeContext.header.title = theme.header.title || themeContext.header.title;
 
            if (theme.header.hasOwnProperty("url")) {
                themeContext.header.url = theme.header.url;
            }
 
            if (theme.header.hasOwnProperty("image")) {
                if (theme.header.image) {
                    url = serveFile(themeApp,"/header/",theme.header.image);
                    if (url) {
                        themeContext.header.image = url;
                    }
                } else {
                    themeContext.header.image = null;
                }
            }
        }
 
        if (theme.deployButton) {
            if (theme.deployButton.type == "simple") {
                themeSettings.deployButton = {
                    type: "simple"
                }
                if (theme.deployButton.label) {
                    themeSettings.deployButton.label = theme.deployButton.label;
                }
                if (theme.deployButton.icon) {
                    url = serveFile(themeApp,"/deploy/",theme.deployButton.icon);
                    if (url) {
                        themeSettings.deployButton.icon = url;
                    }
                }
            }
        }
 
        if (theme.hasOwnProperty("userMenu")) {
            themeSettings.userMenu = theme.userMenu;
        }
 
        if (theme.login) {
            if (theme.login.image) {
                url = serveFile(themeApp,"/login/",theme.login.image);
                if (url) {
                    themeContext.login = {
                        image: url
                    }
                }
            }
        }
 
        if (theme.hasOwnProperty("menu")) {
            themeSettings.menu = theme.menu;
        }
 
        if (theme.hasOwnProperty("palette")) {
            themeSettings.palette = theme.palette;
        }
        return themeApp;
    },
    context: function() {
        return themeContext;
    },
    settings: function() {
        return themeSettings;
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/ui.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/ui.js

Statements: 50% (17 / 34)      Branches: 0% (0 / 4)      Functions: 40% (2 / 5)      Lines: 50% (17 / 34)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80                              1 1 1   1   1   1 1   1 1 1   1 1     1   1 1       1 1                                                                          
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
var express = require('express');
var fs = require("fs");
var path = require("path");
 
var theme = require("./theme");
 
var Mustache = require("mustache");
 
var icon_paths = [path.resolve(__dirname + '/../../public/icons')];
var iconCache = {};
//TODO: create a default icon
var defaultIcon = path.resolve(__dirname + '/../../public/icons/arrow-in.png');
var templateDir = path.resolve(__dirname+"/../../editor/templates");
var editorTemplate;
 
function nodeIconDir(dir) {
    icon_paths.push(path.resolve(dir));
}
 
module.exports = {
    init: function(runtime) {
        editorTemplate = fs.readFileSync(path.join(templateDir,"index.mst"),"utf8");
        Mustache.parse(editorTemplate);
        // TODO: this allows init to be called multiple times without
        //       registering multiple instances of the listener.
        //       It isn't.... ideal.
        runtime.events.removeListener("node-icon-dir",nodeIconDir);
        runtime.events.on("node-icon-dir",nodeIconDir);
    },
 
    ensureSlash: function(req,res,next) {
        var parts = req.originalUrl.split("?");
        if (parts[0].slice(-1) != "/") {
            parts[0] += "/";
            var redirect = parts.join("?");
            res.redirect(301,redirect);
        } else {
            next();
        }
    },
    icon: function(req,res) {
        if (iconCache[req.params.icon]) {
            res.sendFile(iconCache[req.params.icon]); // if not found, express prints this to the console and serves 404
        } else {
            for (var p=0;p<icon_paths.length;p++) {
                var iconPath = path.join(icon_paths[p],req.params.icon);
                try {
                    fs.statSync(iconPath);
                    res.sendFile(iconPath);
                    iconCache[req.params.icon] = iconPath;
                    return;
                } catch(err) {
                    // iconPath doesn't exist
                }
            }
            res.sendFile(defaultIcon);
        }
    },
    editor: function(req,res) {
        res.send(Mustache.render(editorTemplate,theme.context()));
    },
    editorResources: express.static(__dirname + '/../../public')
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/

Statements: 32.03% (82 / 256)      Branches: 2.94% (3 / 102)      Functions: 9.8% (5 / 51)      Lines: 31.89% (81 / 254)      Ignored: 2 branches     

All files » node-npmtest-node-red/node_modules/node-red/red/api/auth/
File Statements Branches Functions Lines
clients.js 42.86% (3 / 7) 0% (0 / 2) 0% (0 / 1) 42.86% (3 / 7)
index.js 48.15% (26 / 54) 5.56% (1 / 18) 18.18% (2 / 11) 48.15% (26 / 54)
permissions.js 18.52% (5 / 27) 0% (0 / 24) 0% (0 / 1) 18.52% (5 / 27)
strategies.js 33.33% (23 / 69) 5% (1 / 20) 6.67% (1 / 15) 33.33% (23 / 69)
tokens.js 18% (9 / 50) 0% (0 / 18) 0% (0 / 13) 18.37% (9 / 49)
users.js 32.65% (16 / 49) 5% (1 / 20) 20% (2 / 10) 31.25% (15 / 48)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/clients.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/clients.js

Statements: 42.86% (3 / 7)      Branches: 0% (0 / 2)      Functions: 0% (0 / 1)      Lines: 42.86% (3 / 7)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35                                1   1         1                      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
 
var clients = [
    {id:"node-red-editor",secret:"not_available"},
    {id:"node-red-admin",secret:"not_available"}
];
 
module.exports = {
    get: function(id) {
        for (var i=0;i<clients.length;i++) {
            if (clients[i].id == id) {
                return when.resolve(clients[i]);
            }
        }
        return when.resolve(null);
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/index.js

Statements: 48.15% (26 / 54)      Branches: 5.56% (1 / 18)      Functions: 18.18% (2 / 11)      Lines: 48.15% (26 / 54)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                1 1   1 1 1 1   1   1 1     1 1 1   1   1   1 1 1 1             1 25                                   1           1     1       1                           1                 1                                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var passport = require("passport");
var oauth2orize = require("oauth2orize");
 
var strategies = require("./strategies");
var Tokens = require("./tokens");
var Users = require("./users");
var permissions = require("./permissions");
 
var theme = require("../theme");
 
var settings = null;
var log = null
 
 
passport.use(strategies.bearerStrategy.BearerStrategy);
passport.use(strategies.clientPasswordStrategy.ClientPasswordStrategy);
passport.use(strategies.anonymousStrategy);
 
var server = oauth2orize.createServer();
 
server.exchange(oauth2orize.exchange.password(strategies.passwordTokenExchange));
 
function init(runtime) {
    settings = runtime.settings;
    log = runtime.log;
    Iif (settings.adminAuth) {
        Users.init(settings.adminAuth);
        Tokens.init(settings.adminAuth,runtime.storage);
        strategies.init(runtime);
    }
}
 
function needsPermission(permission) {
    return function(req,res,next) {
        if (settings && settings.adminAuth) {
            return passport.authenticate(['bearer','anon'],{ session: false })(req,res,function() {
                if (!req.user) {
                    return next();
                }
                if (permissions.hasPermission(req.authInfo.scope,permission)) {
                    return next();
                }
                log.audit({event: "permission.fail", permissions: permission},req);
                return res.status(401).end();
            });
        } else {
            next();
        }
    }
}
 
function ensureClientSecret(req,res,next) {
    if (!req.body.client_secret) {
        req.body.client_secret = 'not_available';
    }
    next();
}
function authenticateClient(req,res,next) {
    return passport.authenticate(['oauth2-client-password'], {session: false})(req,res,next);
}
function getToken(req,res,next) {
    return server.token()(req,res,next);
}
 
function login(req,res) {
    var response = {};
    if (settings.adminAuth) {
        response = {
            "type":"credentials",
            "prompts":[{id:"username",type:"text",label:"Username"},{id:"password",type:"password",label:"Password"}]
        }
        if (theme.context().login && theme.context().login.image) {
            response.image = theme.context().login.image;
        }
    }
    res.json(response);
}
 
function revoke(req,res) {
    var token = req.body.token;
    // TODO: audit log
    Tokens.revoke(token).then(function() {
        log.audit({event: "auth.login.revoke"},req);
        res.status(200).end();
    });
}
 
module.exports = {
    init: init,
    needsPermission: needsPermission,
    ensureClientSecret: ensureClientSecret,
    authenticateClient: authenticateClient,
    getToken: getToken,
    errorHandler: function(err,req,res,next) {
        //TODO: audit log statment
        //console.log(err.stack);
        //log.log({level:"audit",type:"auth",msg:err.toString()});
        return server.errorHandler()(err,req,res,next);
    },
    login: login,
    revoke: revoke
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/permissions.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/permissions.js

Statements: 18.52% (5 / 27)      Branches: 0% (0 / 24)      Functions: 0% (0 / 1)      Lines: 18.52% (5 / 27)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67                                1   1 1   1                                                                                 1        
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var util = require('util');
 
var readRE = /^((.+)\.)?read$/
var writeRE = /^((.+)\.)?write$/
 
function hasPermission(userScope,permission) {
    if (permission === "") {
        return true;
    }
    var i;
 
    if (util.isArray(permission)) {
        // Multiple permissions requested - check each one
        for (i=0;i<permission.length;i++) {
            if (!hasPermission(userScope,permission[i])) {
                return false;
            }
        }
        // All permissions check out
        return true;
    }
 
    if (util.isArray(userScope)) {
        if (userScope.length === 0) {
            return false;
        }
        for (i=0;i<userScope.length;i++) {
            if (hasPermission(userScope[i],permission)) {
                return true;
            }
        }
        return false;
    }
 
    if (userScope === "*" || userScope === permission) {
        return true;
    }
 
    if (userScope === "read" || userScope === "*.read") {
        return readRE.test(permission);
    } else if (userScope === "write" || userScope === "*.write") {
        return writeRE.test(permission);
    }
    return false;
}
 
module.exports = {
    hasPermission: hasPermission,
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/strategies.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/strategies.js

Statements: 33.33% (23 / 69)      Branches: 5% (1 / 20)      Functions: 6.67% (1 / 15)      Lines: 33.33% (23 / 69)      Ignored: 1 branch     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136                                1 1   1 1 1   1 1 1 1   1   1                                   1   1                   1   1 1     1                                                                                     1 1 1   1 1                     1                    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var BearerStrategy = require('passport-http-bearer').Strategy;
var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy;
 
var passport = require("passport");
var crypto = require("crypto");
var util = require("util");
 
var Tokens = require("./tokens");
var Users = require("./users");
var Clients = require("./clients");
var permissions = require("./permissions");
 
var log;
 
var bearerStrategy = function (accessToken, done) {
    // is this a valid token?
    Tokens.get(accessToken).then(function(token) {
        if (token) {
            Users.get(token.user).then(function(user) {
                if (user) {
                    done(null,user,{scope:token.scope});
                } else {
                    log.audit({event: "auth.invalid-token"});
                    done(null,false);
                }
            });
        } else {
            log.audit({event: "auth.invalid-token"});
            done(null,false);
        }
    });
}
bearerStrategy.BearerStrategy = new BearerStrategy(bearerStrategy);
 
var clientPasswordStrategy = function(clientId, clientSecret, done) {
    Clients.get(clientId).then(function(client) {
        if (client && client.secret == clientSecret) {
            done(null,client);
        } else {
            log.audit({event: "auth.invalid-client",client:clientId});
            done(null,false);
        }
    });
}
clientPasswordStrategy.ClientPasswordStrategy = new ClientPasswordStrategy(clientPasswordStrategy);
 
var loginAttempts = [];
var loginSignInWindow = 600000; // 10 minutes
 
 
var passwordTokenExchange = function(client, username, password, scope, done) {
    var now = Date.now();
    loginAttempts = loginAttempts.filter(function(logEntry) {
        return logEntry.time + loginSignInWindow > now;
    });
    loginAttempts.push({time:now, user:username});
    var attemptCount = 0;
    loginAttempts.forEach(function(logEntry) {
        /* istanbul ignore else */
        if (logEntry.user == username) {
            attemptCount++;
        }
    });
    if (attemptCount > 5) {
        log.audit({event: "auth.login.fail.too-many-attempts",username:username,client:client.id});
        done(new Error("Too many login attempts. Wait 10 minutes and try again"),false);
        return;
    }
 
    Users.authenticate(username,password).then(function(user) {
        if (user) {
            if (scope === "") {
                scope = user.permissions;
            }
            if (permissions.hasPermission(user.permissions,scope)) {
                loginAttempts = loginAttempts.filter(function(logEntry) {
                    return logEntry.user !== username;
                });
                Tokens.create(username,client.id,scope).then(function(tokens) {
                    log.audit({event: "auth.login",username:username,client:client.id,scope:scope});
                    done(null,tokens.accessToken,null,{expires_in:tokens.expires_in});
                });
            } else {
                log.audit({event: "auth.login.fail.permissions",username:username,client:client.id,scope:scope});
                done(null,false);
            }
        } else {
            log.audit({event: "auth.login.fail.credentials",username:username,client:client.id,scope:scope});
            done(null,false);
        }
    });
}
 
function AnonymousStrategy() {
  passport.Strategy.call(this);
  this.name = 'anon';
}
util.inherits(AnonymousStrategy, passport.Strategy);
AnonymousStrategy.prototype.authenticate = function(req) {
    var self = this;
    Users.default().then(function(anon) {
        if (anon) {
            self.success(anon,{scope:anon.permissions});
        } else {
            self.fail(401);
        }
    });
}
 
module.exports = {
    init: function(runtime) {
        log = runtime.log;
    },
    bearerStrategy: bearerStrategy,
    clientPasswordStrategy: clientPasswordStrategy,
    passwordTokenExchange: passwordTokenExchange,
    anonymousStrategy: new AnonymousStrategy()
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/tokens.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/tokens.js

Statements: 18% (9 / 50)      Branches: 0% (0 / 18)      Functions: 0% (0 / 13)      Lines: 18.37% (9 / 49)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114                                1   1                   1   1   1   1   1                                   1                   1                                                                                                  
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
 
function generateToken(length) {
    var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
    var token = [];
    for (var i=0;i<length;i++) {
        token.push(c[Math.floor(Math.random()*c.length)]);
    }
    return token.join("");
}
 
 
var storage;
 
var sessionExpiryTime
 
var sessions = {};
 
var loadedSessions = null;
 
function expireSessions() {
    var now = Date.now();
    var modified = false;
    for (var t in sessions) {
        if (sessions.hasOwnProperty(t)) {
            var session = sessions[t];
            if (!session.hasOwnProperty("expires") || session.expires < now) {
                delete sessions[t];
                modified = true;
            }
        }
    }
    if (modified) {
        return storage.saveSessions(sessions);
    } else {
        return when.resolve();
    }
}
function loadSessions() {
    if (loadedSessions === null) {
        loadedSessions = storage.getSessions().then(function(_sessions) {
             sessions = _sessions||{};
             return expireSessions();
        });
    }
    return loadedSessions;
}
 
module.exports = {
    init: function(adminAuthSettings, _storage) {
        storage = _storage;
        sessionExpiryTime = adminAuthSettings.sessionExpiryTime || 604800; // 1 week in seconds
        // At this point, storage will not have been initialised, so defer loading
        // the sessions until there's a request for them.
        loadedSessions = null;
        return when.resolve();
    },
    get: function(token) {
        return loadSessions().then(function() {
            if (sessions[token]) {
                if (sessions[token].expires < Date.now()) {
                    return expireSessions().then(function() { return null });
                }
            }
            return when.resolve(sessions[token]);
        });
    },
    create: function(user,client,scope) {
        return loadSessions().then(function() {
            var accessToken = generateToken(128);
 
            var accessTokenExpiresAt = Date.now() + (sessionExpiryTime*1000);
 
            var session = {
                user:user,
                client:client,
                scope:scope,
                accessToken: accessToken,
                expires: accessTokenExpiresAt
            };
            sessions[accessToken] = session;
            return storage.saveSessions(sessions).then(function() {
                return {
                    accessToken: accessToken,
                    expires_in: sessionExpiryTime
                }
            });
        });
    },
    revoke: function(token) {
        return loadSessions().then(function() {
            delete sessions[token];
            return storage.saveSessions(sessions);
        });
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/users.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/api/auth/users.js

Statements: 32.65% (16 / 49)      Branches: 5% (1 / 20)      Functions: 20% (2 / 10)      Lines: 31.25% (15 / 48)      Ignored: 1 branch     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103                                1 1 1 1   1 1 1   1                     1     1 1     1           1                                                                                           1       1      
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
 
var when = require("when");
var util = require("util");
var bcrypt;
try { bcrypt = require('bcrypt'); }
catch(e) { bcrypt = require('bcryptjs'); }
var users = {};
var passwords = {};
var defaultUser = null;
 
function authenticate(username,password) {
    var user = users[username];
    if (user) {
        return when.promise(function(resolve,reject) {
            bcrypt.compare(password, passwords[username], function(err, res) {
                resolve(res?user:null);
            });
        });
    }
    return when.resolve(null);
}
function get(username) {
    return when.resolve(users[username]);
}
function getDefaultUser() {
    return when.resolve(null);
}
 
var api = {
    get: get,
    authenticate: authenticate,
    default: getDefaultUser
}
 
function init(config) {
    users = {};
    passwords = {};
    defaultUser = null;
    if (config.type == "credentials") {
        if (config.users) {
            if (typeof config.users === "function") {
                api.get = config.users;
            } else {
                var us = config.users;
                /* istanbul ignore else */
                if (!util.isArray(us)) {
                    us = [us];
                }
                for (var i=0;i<us.length;i++) {
                    var u = us[i];
                    users[u.username] = {
                        "username":u.username,
                        "permissions":u.permissions
                    };
                    passwords[u.username] = u.password;
                }
            }
        }
        if (config.authenticate && typeof config.authenticate === "function") {
            api.authenticate = config.authenticate;
        } else {
            api.authenticate = authenticate;
        }
    }
    if (config.default) {
        if (typeof config.default === "function") {
            api.default = config.default;
        } else {
            api.default = function() {
                return when.resolve({
                    "anonymous": true,
                    "permissions":config.default.permissions
                });
            }
        }
    } else {
        api.default = getDefaultUser;
    }
}
 
module.exports = {
    init: init,
    get: function(username) { return api.get(username) },
    authenticate: function(username,password) { return api.authenticate(username,password) },
    default: function() { return api.default(); }
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/

Statements: 40.29% (193 / 479)      Branches: 19.94% (62 / 311)      Functions: 58.67% (44 / 75)      Lines: 40% (190 / 475)      Ignored: 3 branches     

All files » node-npmtest-node-red/node_modules/node-red/red/runtime/
File Statements Branches Functions Lines
events.js 100% (2 / 2) 100% (0 / 0) 100% (0 / 0) 100% (2 / 2)
i18n.js 66.07% (37 / 56) 27.27% (6 / 22) 84.62% (11 / 13) 66.07% (37 / 56)
index.js 66.67% (64 / 96) 38.24% (13 / 34) 48% (12 / 25) 65.96% (62 / 94)
log.js 76.67% (46 / 60) 50.88% (29 / 57) 81.25% (13 / 16) 76.67% (46 / 60)
settings.js 61.54% (32 / 52) 54.17% (13 / 24) 72.73% (8 / 11) 62% (31 / 50)
util.js 5.63% (12 / 213) 0.57% (1 / 174) 0% (0 / 10) 5.63% (12 / 213)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/events.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/events.js

Statements: 100% (2 / 2)      Branches: 100% (0 / 0)      Functions: 100% (0 / 0)      Lines: 100% (2 / 2)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21                                1   1    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var events = require("events"); 
 
module.exports = new events.EventEmitter(); 
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/i18n.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/i18n.js

Statements: 66.07% (37 / 56)      Branches: 27.27% (6 / 22)      Functions: 84.62% (11 / 13)      Lines: 66.07% (37 / 56)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134                                1 1 1 1   1   1 1   1 1 3   1     1 9 9 9 9         1                       1   18 18   18 18 9   9 9 9   9     9                         1 1 1 1             1         1                                 1                 1           15      
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var i18n = require("i18next");
var when = require("when");
var path = require("path");
var fs = require("fs");
 
var defaultLang = "en-US";
 
var resourceMap = {};
var resourceCache = {};
 
function registerMessageCatalogs(catalogs) {
    var promises = catalogs.map(function(catalog) {
        return registerMessageCatalog(catalog.namespace,catalog.dir,catalog.file);
    });
    return when.settle(promises);
}
 
function registerMessageCatalog(namespace,dir,file) {
    return when.promise(function(resolve,reject) {
        resourceMap[namespace] = { basedir:dir, file:file};
        i18n.loadNamespace(namespace,function() {
            resolve();
        });
    });
}
 
function mergeCatalog(fallback,catalog) {
    for (var k in fallback) {
        if (fallback.hasOwnProperty(k)) {
            if (!catalog[k]) {
                catalog[k] = fallback[k];
            } else if (typeof fallback[k] === 'object') {
                mergeCatalog(fallback[k],catalog[k]);
            }
        }
    }
}
 
var MessageFileLoader = {
    fetchOne: function(lng, ns, callback) {
        Eif (resourceMap[ns]) {
            var file = path.join(resourceMap[ns].basedir,lng,resourceMap[ns].file);
            //console.log(file);
            fs.readFile(file,"utf8",function(err,content) {
                if (err) {
                    callback(err);
                } else {
                    try {
                        resourceCache[ns] = resourceCache[ns]||{};
                        resourceCache[ns][lng] = JSON.parse(content.replace(/^\uFEFF/, ''));
                        //console.log(resourceCache[ns][lng]);
                        Iif (lng !== defaultLang) {
                            mergeCatalog(resourceCache[ns][defaultLang],resourceCache[ns][lng]);
                        }
                        callback(null, resourceCache[ns][lng]);
                    } catch(e) {
                        callback(e);
                    }
                }
            });
        } else {
            callback(new Error("Unrecognised namespace"));
        }
    }
 
}
 
function init() {
    return when.promise(function(resolve,reject) {
        i18n.backend(MessageFileLoader);
        i18n.init({
            ns: {
                namespaces: [],
                defaultNs: "runtime"
            },
            fallbackLng: [defaultLang]
        },function() {
            resolve();
        });
    });
}
 
function getCatalog(namespace,lang) {
    var result = null;
    if (resourceCache.hasOwnProperty(namespace)) {
        result = resourceCache[namespace][lang];
        if (!result) {
            var langParts = lang.split("-");
            if (langParts.length == 2) {
                result = resourceCache[namespace][langParts[0]];
            }
            if (!result) {
                return resourceCache[namespace][defaultLang];
            }
        }
    }
    return result;
}
 
var obj = module.exports = {
    init: init,
    registerMessageCatalog: registerMessageCatalog,
    registerMessageCatalogs: registerMessageCatalogs,
    catalog: getCatalog,
    i: i18n,
    defaultLang: defaultLang
}
 
obj['_'] = function() {
    //var opts = {};
    //if (def) {
    //    opts.defaultValue = def;
    //}
    //console.log(arguments);
    return i18n.t.apply(null,arguments);
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/index.js

Statements: 66.67% (64 / 96)      Branches: 38.24% (13 / 34)      Functions: 48% (12 / 25)      Lines: 65.96% (62 / 94)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209                                1   1 1 1 1 1 1   1 1 1 1   1   1   1           1                           1   1 1 1 1   1   1 1   1     1   1 2 1   1 1           2     1 1   1   1 1     1         1 1 1   1 1           1 1   1 37 37 1 1 1 1   1   1                                               1 1   1 1             1                                       1                 1                           224 38            
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require('when');
 
var redNodes = require("./nodes");
var storage = require("./storage");
var log = require("./log");
var i18n = require("./i18n");
var events = require("./events");
var settings = require("./settings");
 
var express = require("express");
var path = require('path');
var fs = require("fs");
var os = require("os");
 
var runtimeMetricInterval = null;
 
var started = false;
 
var stubbedExpressApp = {
    get: function() {},
    post: function() {},
    put: function() {},
    delete: function() {}
}
var adminApi = {
    library: {
        register: function() {}
    },
    auth: {
        needsPermission: function() {}
    },
    comms: {
        publish: function() {}
    },
    adminApp: stubbedExpressApp,
    server: {}
}
 
var nodeApp;
 
function init(userSettings,_adminApi) {
    userSettings.version = getVersion();
    log.init(userSettings);
    settings.init(userSettings);
 
    nodeApp = express();
 
    Eif (_adminApi) {
        adminApi = _adminApi;
    }
    redNodes.init(runtime);
}
 
var version;
 
function getVersion() {
    if (!version) {
        version = require(path.join(__dirname,"..","..","package.json")).version;
        /* istanbul ignore else */
        try {
            fs.statSync(path.join(__dirname,"..","..",".git"));
            version += "-git";
        } catch(err) {
            // No git directory
        }
    }
    return version;
}
 
function start() {
    return i18n.init()
        .then(function() {
            return i18n.registerMessageCatalog("runtime",path.resolve(path.join(__dirname,"locales")),"runtime.json")
        })
        .then(function() { return storage.init(runtime)})
        .then(function() { return settings.load(storage)})
        .then(function() {
 
            Iif (log.metric()) {
                runtimeMetricInterval = setInterval(function() {
                    reportMetrics();
                }, settings.runtimeMetricInterval||15000);
            }
            log.info("\n\n"+log._("runtime.welcome")+"\n===================\n");
            Eif (settings.version) {
                log.info(log._("runtime.version",{component:"Node-RED",version:"v"+settings.version}));
            }
            log.info(log._("runtime.version",{component:"Node.js ",version:process.version}));
            Iif (settings.UNSUPPORTED_VERSION) {
                log.error("*****************************************************************");
                log.error("* "+log._("runtime.unsupported_version",{component:"Node.js",version:process.version,requires: ">=4"})+" *");
                log.error("*****************************************************************");
                events.emit("runtime-event",{id:"runtime-unsupported-version",type:"error",text:"notification.errors.unsupportedVersion"});
            }
            log.info(os.type()+" "+os.release()+" "+os.arch()+" "+os.endianness());
            return redNodes.load().then(function() {
 
                var i;
                var nodeErrors = redNodes.getNodeList(function(n) { return n.err!=null;});
                var nodeMissing = redNodes.getNodeList(function(n) { return n.module && n.enabled && !n.loaded && !n.err;});
                Eif (nodeErrors.length > 0) {
                    log.warn("------------------------------------------------------");
                    for (i=0;i<nodeErrors.length;i+=1) {
                        log.warn("["+nodeErrors[i].name+"] "+nodeErrors[i].err);
                    }
                    log.warn("------------------------------------------------------");
                }
                Iif (nodeMissing.length > 0) {
                    log.warn(log._("server.missing-modules"));
                    var missingModules = {};
                    for (i=0;i<nodeMissing.length;i++) {
                        var missing = nodeMissing[i];
                        missingModules[missing.module] = (missingModules[missing.module]||[]).concat(missing.types);
                    }
                    var promises = [];
                    for (i in missingModules) {
                        if (missingModules.hasOwnProperty(i)) {
                            log.warn(" - "+i+": "+missingModules[i].join(", "));
                            if (settings.autoInstallModules && i != "node-red") {
                                redNodes.installModule(i).otherwise(function(err) {
                                    // Error already reported. Need the otherwise handler
                                    // to stop the error propagating any further
                                });
                            }
                        }
                    }
                    if (!settings.autoInstallModules) {
                        log.info(log._("server.removing-modules"));
                        redNodes.cleanModuleList();
                    }
                }
                Eif (settings.settingsFile) {
                    log.info(log._("runtime.paths.settings",{path:settings.settingsFile}));
                }
                redNodes.loadFlows().then(redNodes.startFlows);
                started = true;
            }).otherwise(function(err) {
                console.log(err);
            });
        });
}
 
function reportMetrics() {
    var memUsage = process.memoryUsage();
 
    log.log({
        level: log.METRIC,
        event: "runtime.memory.rss",
        value: memUsage.rss
    });
    log.log({
        level: log.METRIC,
        event: "runtime.memory.heapTotal",
        value: memUsage.heapTotal
    });
    log.log({
        level: log.METRIC,
        event: "runtime.memory.heapUsed",
        value: memUsage.heapUsed
    });
}
 
function stop() {
    if (runtimeMetricInterval) {
        clearInterval(runtimeMetricInterval);
        runtimeMetricInterval = null;
    }
    started = false;
    return redNodes.stopFlows();
}
 
var runtime = module.exports = {
    init: init,
    start: start,
    stop: stop,
 
    version: getVersion,
 
    log: log,
    i18n: i18n,
    settings: settings,
    storage: storage,
    events: events,
    nodes: redNodes,
    util: require("./util"),
    get adminApi() { return adminApi },
    get nodeApp() { return nodeApp },
    isStarted: function() {
        return started;
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/log.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/log.js

Statements: 76.67% (46 / 60)      Branches: 50.88% (29 / 57)      Functions: 81.25% (13 / 16)      Lines: 76.67% (46 / 60)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161                                1 1   1   1                       1                     1   1   1 1 1 1   1   1 1 20 17       1   1 20         1 17     17 17     17       1                     1 1 1 1 1 1     1 1 1 1 1                 2                 20 20 35       11     4     2           3     1                           1    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var util = require("util");
var EventEmitter = require("events").EventEmitter;
 
var i18n = require("./i18n");
 
var levels = {
    off:    1,
    fatal:  10,
    error:  20,
    warn:   30,
    info:   40,
    debug:  50,
    trace:  60,
    audit:  98,
    metric: 99
};
 
var levelNames = {
    10: "fatal",
    20: "error",
    30: "warn",
    40: "info",
    50: "debug",
    60: "trace",
    98: "audit",
    99: "metric"
};
 
var logHandlers = [];
 
var metricsEnabled = false;
 
var LogHandler = function(settings) {
    this.logLevel  = settings ? levels[settings.level]||levels.info : levels.info;
    this.metricsOn = settings ? settings.metrics||false : false;
    this.auditOn = settings ? settings.audit||false : false;
 
    metricsEnabled = metricsEnabled || this.metricsOn;
 
    this.handler   = (settings && settings.handler) ? settings.handler(settings) : consoleLogger;
    this.on("log",function(msg) {
        if (this.shouldReportMessage(msg.level)) {
            this.handler(msg);
        }
    });
}
util.inherits(LogHandler, EventEmitter);
 
LogHandler.prototype.shouldReportMessage = function(msglevel) {
    return (msglevel == log.METRIC && this.metricsOn) ||
           (msglevel == log.AUDIT && this.auditOn) ||
           msglevel <= this.logLevel;
}
 
var consoleLogger = function(msg) {
    Iif (msg.level == log.METRIC || msg.level == log.AUDIT) {
        util.log("["+levelNames[msg.level]+"] "+JSON.stringify(msg));
    } else {
        var message = msg.msg;
        Iif (typeof message === 'object' && message.toString() === '[object Object]' && message.message) {
            message = message.message;
        }
        util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+message);
    }
}
 
var log = module.exports = {
    FATAL:  10,
    ERROR:  20,
    WARN:   30,
    INFO:   40,
    DEBUG:  50,
    TRACE:  60,
    AUDIT:  98,
    METRIC: 99,
 
    init: function(settings) {
        metricsEnabled = false;
        logHandlers = [];
        var loggerSettings = {};
        Eif (settings.logging) {
            var keys = Object.keys(settings.logging);
            Iif (keys.length === 0) {
                log.addHandler(new LogHandler());
            } else {
                for (var i=0, l=keys.length; i<l; i++) {
                    var config = settings.logging[keys[i]];
                    loggerSettings = config || {};
                    Eif ((keys[i] === "console") || config.handler) {
                        log.addHandler(new LogHandler(loggerSettings));
                    }
                }
            }
        } else {
            log.addHandler(new LogHandler());
        }
    },
    addHandler: function(func) {
        logHandlers.push(func);
    },
    removeHandler: function(func) {
        var index = logHandlers.indexOf(func);
        if (index > -1) {
            logHandlers.splice(index,1);
        }
    },
    log: function(msg) {
        msg.timestamp = Date.now();
        logHandlers.forEach(function(handler) {
            handler.emit("log",msg);
        });
    },
    info: function(msg) {
        log.log({level:log.INFO,msg:msg});
    },
    warn: function(msg) {
        log.log({level:log.WARN,msg:msg});
    },
    error: function(msg) {
        log.log({level:log.ERROR,msg:msg});
    },
    trace: function(msg) {
        log.log({level:log.TRACE,msg:msg});
    },
    debug: function(msg) {
        log.log({level:log.DEBUG,msg:msg});
    },
    metric: function() {
        return metricsEnabled;
    },
 
    audit: function(msg,req) {
        msg.level = log.AUDIT;
        if (req) {
            msg.user = req.user;
            msg.path = req.path;
            msg.ip = (req.headers && req.headers['x-forwarded-for']) || (req.connection && req.connection.remoteAddress) || undefined;
        }
        log.log(msg);
    }
}
 
log["_"] = i18n._;
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/settings.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/settings.js

Statements: 61.54% (32 / 52)      Branches: 54.17% (13 / 24)      Functions: 72.73% (8 / 11)      Lines: 62% (31 / 50)      Ignored: 2 branches     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107                                1 1 1 1   1 1 1   1   1 1   16   16 16 167 16       1     1 1 1       3     3     3       1     1     1 1 1 1 1                                       3                               1    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var clone = require("clone");
var assert = require("assert");
var log = require("./log");
 
var userSettings = null;
var globalSettings = null;
var storage = null;
 
var persistentSettings = {
    init: function(settings) {
        userSettings = settings;
        for (var i in settings) {
            /* istanbul ignore else */
            Eif (settings.hasOwnProperty(i) && i !== 'load' && i !== 'get' && i !== 'set' && i !== 'available' && i !== 'reset') {
                // Don't allow any of the core functions get replaced via settings
                (function() {
                    var j = i;
                    persistentSettings.__defineGetter__(j,function() { return userSettings[j]; });
                    persistentSettings.__defineSetter__(j,function() { throw new Error("Property '"+j+"' is read-only"); });
                })();
            }
        }
        globalSettings = null;
    },
    load: function(_storage) {
        storage = _storage;
        return storage.getSettings().then(function(_settings) {
            globalSettings = _settings;
        });
    },
    get: function(prop) {
        Iif (userSettings.hasOwnProperty(prop)) {
            return clone(userSettings[prop]);
        }
        Iif (globalSettings === null) {
            throw new Error(log._("settings.not-available"));
        }
        return clone(globalSettings[prop]);
    },
 
    set: function(prop,value) {
        Iif (userSettings.hasOwnProperty(prop)) {
            throw new Error(log._("settings.property-read-only", {prop:prop}));
        }
        Iif (globalSettings === null) {
            throw new Error(log._("settings.not-available"));
        }
        var current = globalSettings[prop];
        globalSettings[prop] = value;
        try {
            assert.deepEqual(current,value);
            return when.resolve();
        } catch(err) {
            return storage.saveSettings(globalSettings);
        }
    },
    delete: function(prop) {
        if (userSettings.hasOwnProperty(prop)) {
            throw new Error(log._("settings.property-read-only", {prop:prop}));
        }
        if (globalSettings === null) {
            throw new Error(log._("settings.not-available"));
        }
        if (globalSettings.hasOwnProperty(prop)) {
            delete globalSettings[prop];
            return storage.saveSettings(globalSettings);
        }
        return when.resolve();
    },
 
    available: function() {
        return (globalSettings !== null);
    },
 
    reset: function() {
        for (var i in userSettings) {
            /* istanbul ignore else */
            if (userSettings.hasOwnProperty(i)) {
                delete persistentSettings[i];
            }
        }
        userSettings = null;
        globalSettings = null;
        storage = null;
    }
}
 
module.exports = persistentSettings;
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/util.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/util.js

Statements: 5.63% (12 / 213)      Branches: 0.57% (1 / 174)      Functions: 0% (0 / 10)      Lines: 5.63% (12 / 213)      Ignored: 1 branch     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344                                1 1   1       1                     1                     1                                     1                                                                                                                                   1                                                                                                                                                                                                               1                           1                                                                                                                 1                                                   1                        
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var clone = require("clone");
var jsonata = require("jsonata");
 
function generateId() {
    return (1+Math.random()*4294967295).toString(16);
}
 
function ensureString(o) {
    if (Buffer.isBuffer(o)) {
        return o.toString();
    } else if (typeof o === "object") {
        return JSON.stringify(o);
    } else if (typeof o === "string") {
        return o;
    }
    return ""+o;
}
 
function ensureBuffer(o) {
    if (Buffer.isBuffer(o)) {
        return o;
    } else if (typeof o === "object") {
        o = JSON.stringify(o);
    } else if (typeof o !== "string") {
        o = ""+o;
    }
    return new Buffer(o);
}
 
function cloneMessage(msg) {
    // Temporary fix for #97
    // TODO: remove this http-node-specific fix somehow
    var req = msg.req;
    var res = msg.res;
    delete msg.req;
    delete msg.res;
    var m = clone(msg);
    if (req) {
        m.req = req;
        msg.req = req;
    }
    if (res) {
        m.res = res;
        msg.res = res;
    }
    return m;
}
 
function compareObjects(obj1,obj2) {
    var i;
    if (obj1 === obj2) {
        return true;
    }
    if (obj1 == null || obj2 == null) {
        return false;
    }
 
    var isArray1 = Array.isArray(obj1);
    var isArray2 = Array.isArray(obj2);
    if (isArray1 != isArray2) {
        return false;
    }
    if (isArray1 && isArray2) {
        if (obj1.length !== obj2.length) {
            return false;
        }
        for (i=0;i<obj1.length;i++) {
            if (!compareObjects(obj1[i],obj2[i])) {
                return false;
            }
        }
        return true;
    }
    var isBuffer1 = Buffer.isBuffer(obj1);
    var isBuffer2 = Buffer.isBuffer(obj2);
    if (isBuffer1 != isBuffer2) {
        return false;
    }
    if (isBuffer1 && isBuffer2) {
        if (obj1.equals) {
            // For node 0.12+ - use the native equals
            return obj1.equals(obj2);
        } else {
            if (obj1.length !== obj2.length) {
                return false;
            }
            for (i=0;i<obj1.length;i++) {
                if (obj1.readUInt8(i) !== obj2.readUInt8(i)) {
                    return false;
                }
            }
            return true;
        }
    }
 
    if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
        return false;
    }
    var keys1 = Object.keys(obj1);
    var keys2 = Object.keys(obj2);
    if (keys1.length != keys2.length) {
        return false;
    }
    for (var k in obj1) {
        /* istanbul ignore else */
        if (obj1.hasOwnProperty(k)) {
            if (!compareObjects(obj1[k],obj2[k])) {
                return false;
            }
        }
    }
    return true;
}
 
function normalisePropertyExpression(str) {
    // This must be kept in sync with validatePropertyExpression
    // in editor/js/ui/utils.js
 
    var length = str.length;
    if (length === 0) {
        throw new Error("Invalid property expression: zero-length");
    }
    var parts = [];
    var start = 0;
    var inString = false;
    var inBox = false;
    var quoteChar;
    var v;
    for (var i=0;i<length;i++) {
        var c = str[i];
        if (!inString) {
            if (c === "'" || c === '"') {
                if (i != start) {
                    throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
                }
                inString = true;
                quoteChar = c;
                start = i+1;
            } else if (c === '.') {
                if (i===0) {
                    throw new Error("Invalid property expression: unexpected . at position 0");
                }
                if (start != i) {
                    v = str.substring(start,i);
                    if (/^\d+$/.test(v)) {
                        parts.push(parseInt(v));
                    } else {
                        parts.push(v);
                    }
                }
                if (i===length-1) {
                    throw new Error("Invalid property expression: unterminated expression");
                }
                // Next char is first char of an identifier: a-z 0-9 $ _
                if (!/[a-z0-9\$\_]/i.test(str[i+1])) {
                    throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
                }
                start = i+1;
            } else if (c === '[') {
                if (i === 0) {
                    throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
                }
                if (start != i) {
                    parts.push(str.substring(start,i));
                }
                if (i===length-1) {
                    throw new Error("Invalid property expression: unterminated expression");
                }
                // Next char is either a quote or a number
                if (!/["'\d]/.test(str[i+1])) {
                    throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
                }
                start = i+1;
                inBox = true;
            } else if (c === ']') {
                if (!inBox) {
                    throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
                }
                if (start != i) {
                    v = str.substring(start,i);
                    if (/^\d+$/.test(v)) {
                        parts.push(parseInt(v));
                    } else {
                        throw new Error("Invalid property expression: unexpected array expression at position "+start);
                    }
                }
                start = i+1;
                inBox = false;
            } else if (c === ' ') {
                throw new Error("Invalid property expression: unexpected ' ' at position "+i);
            }
        } else {
            if (c === quoteChar) {
                if (i-start === 0) {
                    throw new Error("Invalid property expression: zero-length string at position "+start);
                }
                parts.push(str.substring(start,i));
                // If inBox, next char must be a ]. Otherwise it may be [ or .
                if (inBox && !/\]/.test(str[i+1])) {
                    throw new Error("Invalid property expression: unexpected array expression at position "+start);
                } else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) {
                    throw new Error("Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
                }
                start = i+1;
                inString = false;
            }
        }
 
    }
    if (inBox || inString) {
        throw new Error("Invalid property expression: unterminated expression");
    }
    if (start < length) {
        parts.push(str.substring(start));
    }
    return parts;
}
 
function getMessageProperty(msg,expr) {
    var result = null;
    if (expr.indexOf('msg.')===0) {
        expr = expr.substring(4);
    }
    var msgPropParts = normalisePropertyExpression(expr);
    var m;
    msgPropParts.reduce(function(obj, key) {
        result = (typeof obj[key] !== "undefined" ? obj[key] : undefined);
        return result;
    }, msg);
    return result;
}
 
function setMessageProperty(msg,prop,value,createMissing) {
    if (typeof createMissing === 'undefined') {
        createMissing = (typeof value !== 'undefined');
    }
    if (prop.indexOf('msg.')===0) {
        prop = prop.substring(4);
    }
    var msgPropParts = normalisePropertyExpression(prop);
    var depth = 0;
    var length = msgPropParts.length;
    var obj = msg;
    var key;
    for (var i=0;i<length-1;i++) {
        key = msgPropParts[i];
        if (typeof key === 'string' || (typeof key === 'number' && !Array.isArray(obj))) {
            if (obj.hasOwnProperty(key)) {
                obj = obj[key];
            } else if (createMissing) {
                if (typeof msgPropParts[i+1] === 'string') {
                    obj[key] = {};
                } else {
                    obj[key] = [];
                }
                obj = obj[key];
            } else {
                return null;
            }
        } else if (typeof key === 'number') {
            // obj is an array
            if (obj[key] === undefined) {
                if (createMissing) {
                    if (typeof msgPropParts[i+1] === 'string') {
                        obj[key] = {};
                    } else {
                        obj[key] = [];
                    }
                    obj = obj[key];
                } else {
                    return null
                }
            } else {
                obj = obj[key];
            }
        }
    }
    key = msgPropParts[length-1];
    if (typeof value === "undefined") {
        if (typeof key === 'number' && Array.isArray(obj)) {
            obj.splice(key,1);
        } else {
            delete obj[key]
        }
    } else {
        obj[key] = value;
    }
}
 
function evaluateNodeProperty(value, type, node, msg) {
    if (type === 'str') {
        return ""+value;
    } else if (type === 'num') {
        return Number(value);
    } else if (type === 'json') {
        return JSON.parse(value);
    } else if (type === 're') {
        return new RegExp(value);
    } else if (type === 'date') {
        return Date.now();
    } else if (type === 'msg' && msg) {
        return getMessageProperty(msg,value);
    } else if (type === 'flow' && node) {
        return node.context().flow.get(value);
    } else if (type === 'global' && node) {
        return node.context().global.get(value);
    } else if (type === 'bool') {
        return /^true$/i.test(value);
    } else if (type === 'jsonata') {
        return jsonata(value).evaluate({msg:msg});
    }
    return value;
}
 
 
module.exports = {
    ensureString: ensureString,
    ensureBuffer: ensureBuffer,
    cloneMessage: cloneMessage,
    compareObjects: compareObjects,
    generateId: generateId,
    getMessageProperty: getMessageProperty,
    setMessageProperty: setMessageProperty,
    evaluateNodeProperty: evaluateNodeProperty,
    normalisePropertyExpression: normalisePropertyExpression
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/

Statements: 28.9% (113 / 391)      Branches: 13.33% (24 / 180)      Functions: 17.78% (8 / 45)      Lines: 28.9% (113 / 391)      Ignored: 6 branches     

All files » node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/
File Statements Branches Functions Lines
Node.js 16.31% (23 / 141) 7.69% (6 / 78) 0% (0 / 18) 16.31% (23 / 141)
context.js 38.1% (16 / 42) 15% (3 / 20) 28.57% (2 / 7) 38.1% (16 / 42)
credentials.js 29.68% (46 / 155) 15.15% (10 / 66) 26.67% (4 / 15) 29.68% (46 / 155)
index.js 52.83% (28 / 53) 31.25% (5 / 16) 40% (2 / 5) 52.83% (28 / 53)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/Node.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/Node.js

Statements: 16.31% (23 / 141)      Branches: 7.69% (6 / 78)      Functions: 0% (0 / 18)      Lines: 16.31% (23 / 141)      Ignored: 6 branches     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273                                1 1 1   1 1 1 1   1                             1   1                                             1             1   1                 1                                                             1                                                                                                                                                                             1                             1                         1       1       1                           1                               1     1    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var util = require("util");
var EventEmitter = require("events").EventEmitter;
var when = require("when");
 
var redUtil = require("../util");
var Log = require("../log");
var context = require("./context");
var flows = require("./flows");
 
function Node(n) {
    this.id = n.id;
    this.type = n.type;
    this.z = n.z;
    this._closeCallbacks = [];
 
    if (n.name) {
        this.name = n.name;
    }
    if (n._alias) {
        this._alias = n._alias;
    }
    this.updateWires(n.wires);
}
 
util.inherits(Node, EventEmitter);
 
Node.prototype.updateWires = function(wires) {
    //console.log("UPDATE",this.id);
    this.wires = wires || [];
    delete this._wire;
 
    var wc = 0;
    this.wires.forEach(function(w) {
        wc+=w.length;
    });
    this._wireCount = wc;
    if (wc === 0) {
        // With nothing wired to the node, no-op send
        this.send = function(msg) {}
    } else {
        this.send = Node.prototype.send;
        if (this.wires.length === 1 && this.wires[0].length === 1) {
            // Single wire, so we can shortcut the send when
            // a single message is sent
            this._wire = this.wires[0][0];
        }
    }
 
}
Node.prototype.context = function() {
    if (!this._context) {
         this._context = context.get(this._alias||this.id,this.z);
    }
    return this._context;
}
 
Node.prototype._on = Node.prototype.on;
 
Node.prototype.on = function(event, callback) {
    var node = this;
    if (event == "close") {
        this._closeCallbacks.push(callback);
    } else {
        this._on(event, callback);
    }
};
 
Node.prototype.close = function() {
    var promises = [];
    var node = this;
    for (var i=0;i<this._closeCallbacks.length;i++) {
        var callback = this._closeCallbacks[i];
        if (callback.length == 1) {
            promises.push(
                when.promise(function(resolve) {
                    callback.call(node, function() {
                        resolve();
                    });
                })
            );
        } else {
            callback.call(node);
        }
    }
    if (promises.length > 0) {
        return when.settle(promises).then(function() {
            if (this._context) {
                 context.delete(this._alias||this.id,this.z);
            }
        });
    } else {
        if (this._context) {
             context.delete(this._alias||this.id,this.z);
        }
        return;
    }
};
 
Node.prototype.send = function(msg) {
    var msgSent = false;
    var node;
 
    if (msg === null || typeof msg === "undefined") {
        return;
    } else if (!util.isArray(msg)) {
        if (this._wire) {
            // A single message and a single wire on output 0
            // TODO: pre-load flows.get calls - cannot do in constructor
            //       as not all nodes are defined at that point
            if (!msg._msgid) {
                msg._msgid = redUtil.generateId();
            }
            this.metric("send",msg);
            node = flows.get(this._wire);
            /* istanbul ignore else */
            if (node) {
                node.receive(msg);
            }
            return;
        } else {
            msg = [msg];
        }
    }
 
    var numOutputs = this.wires.length;
 
    // Build a list of send events so that all cloning is done before
    // any calls to node.receive
    var sendEvents = [];
 
    var sentMessageId = null;
 
    // for each output of node eg. [msgs to output 0, msgs to output 1, ...]
    for (var i = 0; i < numOutputs; i++) {
        var wires = this.wires[i]; // wires leaving output i
        /* istanbul ignore else */
        if (i < msg.length) {
            var msgs = msg[i]; // msgs going to output i
            if (msgs !== null && typeof msgs !== "undefined") {
                if (!util.isArray(msgs)) {
                    msgs = [msgs];
                }
                var k = 0;
                // for each recipent node of that output
                for (var j = 0; j < wires.length; j++) {
                    node = flows.get(wires[j]); // node at end of wire j
                    if (node) {
                        // for each msg to send eg. [[m1, m2, ...], ...]
                        for (k = 0; k < msgs.length; k++) {
                            var m = msgs[k];
                            if (m !== null && m !== undefined) {
                                /* istanbul ignore else */
                                if (!sentMessageId) {
                                    sentMessageId = m._msgid;
                                }
                                if (msgSent) {
                                    var clonedmsg = redUtil.cloneMessage(m);
                                    sendEvents.push({n:node,m:clonedmsg});
                                } else {
                                    sendEvents.push({n:node,m:m});
                                    msgSent = true;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /* istanbul ignore else */
    if (!sentMessageId) {
        sentMessageId = redUtil.generateId();
    }
    this.metric("send",{_msgid:sentMessageId});
 
    for (i=0;i<sendEvents.length;i++) {
        var ev = sendEvents[i];
        /* istanbul ignore else */
        if (!ev.m._msgid) {
            ev.m._msgid = sentMessageId;
        }
        ev.n.receive(ev.m);
    }
};
 
Node.prototype.receive = function(msg) {
    if (!msg) {
        msg = {};
    }
    if (!msg._msgid) {
        msg._msgid = redUtil.generateId();
    }
    this.metric("receive",msg);
    try {
        this.emit("input", msg);
    } catch(err) {
        this.error(err,msg);
    }
};
 
function log_helper(self, level, msg) {
    var o = {
        level: level,
        id: self.id,
        type: self.type,
        msg: msg
    };
    if (self.name) {
        o.name = self.name;
    }
    Log.log(o);
}
 
Node.prototype.log = function(msg) {
    log_helper(this, Log.INFO, msg);
};
 
Node.prototype.warn = function(msg) {
    log_helper(this, Log.WARN, msg);
};
 
Node.prototype.error = function(logMessage,msg) {
    if (typeof logMessage != 'boolean') {
        logMessage = logMessage || "";
    }
    log_helper(this, Log.ERROR, logMessage);
    /* istanbul ignore else */
    if (msg) {
        flows.handleError(this,logMessage,msg);
    }
};
 
/**
 * If called with no args, returns whether metric collection is enabled
 */
Node.prototype.metric = function(eventname, msg, metricValue) {
    if (typeof eventname === "undefined") {
        return Log.metric();
    }
    var metrics = {};
    metrics.level = Log.METRIC;
    metrics.nodeid = this.id;
    metrics.event = "node."+this.type+"."+eventname;
    metrics.msgid = msg._msgid;
    metrics.value = metricValue;
    Log.log(metrics);
}
 
/**
 * status: { fill:"red|green", shape:"dot|ring", text:"blah" }
 */
Node.prototype.status = function(status) {
    flows.handleStatus(this,status);
};
module.exports = Node;
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/context.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/context.js

Statements: 38.1% (16 / 42)      Branches: 15% (3 / 20)      Functions: 28.57% (2 / 7)      Lines: 38.1% (16 / 42)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83                                1 1 1   1 1 1 1     1     1     1 1   1                                   1             1                         1   1              
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var clone = require("clone");
var when = require("when");
var util = require("../util");
 
function createContext(id,seed) {
    var data = seed || {};
    var obj = seed || {};
    obj.get = function get(key) {
        return util.getMessageProperty(data,key);
    };
    obj.set = function set(key, value) {
        util.setMessageProperty(data,key,value);
    }
    return obj;
}
 
var contexts = {};
var globalContext = null;
 
function getContext(localId,flowId) {
    var contextId = localId;
    if (flowId) {
        contextId = localId+":"+flowId;
    }
    if (contexts.hasOwnProperty(contextId)) {
        return contexts[contextId];
    }
    var newContext = createContext(contextId);
    if (flowId) {
        newContext.flow = getContext(flowId);
        if (globalContext) {
            newContext.global = globalContext;
        }
    }
    contexts[contextId] = newContext;
    return newContext;
}
function deleteContext(id,flowId) {
    var contextId = id;
    if (flowId) {
        contextId = id+":"+flowId;
    }
    delete contexts[contextId];
}
function clean(flowConfig) {
    var activeIds = {};
    var contextId;
    var node;
    for (var id in contexts) {
        if (contexts.hasOwnProperty(id)) {
            var idParts = id.split(":");
            if (!flowConfig.allNodes.hasOwnProperty(idParts[0])) {
                delete contexts[id];
            }
        }
    }
}
module.exports = {
    init: function(settings) {
        globalContext = createContext("global",settings.functionGlobalContext || {});
    },
    get: getContext,
    delete: deleteContext,
    clean:clean
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/credentials.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/credentials.js

Statements: 29.68% (46 / 155)      Branches: 15.15% (10 / 66)      Functions: 26.67% (4 / 15)      Lines: 29.68% (46 / 155)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321                                1 1 1 1   1 1 1 1   1 1 1 1   1                 1   1 1 1 1 1 1             1       1 1 1 1 1 1     1 1   1 1 1       1                                   1                                           1   1 1 1                               1       1 1   1 1                   1                                                                                                                                               5 5                                                                                                                                                                                            
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var crypto = require('crypto');
var settings;
var log;
 
var encryptedCredentials = null;
var credentialCache = {};
var credentialsDef = {};
var dirty = false;
 
var removeDefaultKey = false;
var encryptionEnabled = null;
var encryptionAlgorithm = "aes-256-ctr";
var encryptionKey;
 
function decryptCredentials(key,credentials) {
    var creds = credentials["$"];
    var initVector = new Buffer(creds.substring(0, 32),'hex');
    creds = creds.substring(32);
    var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
    var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
    return JSON.parse(decrypted);
}
 
var api = module.exports = {
    init: function(runtime) {
        log = runtime.log;
        settings = runtime.settings;
        dirty = false;
        credentialCache = {};
        credentialsDef = {};
        encryptionEnabled = null;
    },
 
    /**
     * Sets the credentials from storage.
     */
    load: function (credentials) {
        dirty = false;
        /*
          - if encryptionEnabled === null, check the current configuration
        */
        var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
        var setupEncryptionPromise = when.resolve();
        Eif (encryptionEnabled === null) {
            var defaultKey;
            try {
                defaultKey = settings.get('_credentialSecret');
            } catch(err) {
            }
            Eif (defaultKey) {
                defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
            }
            var userKey;
            try {
                userKey = settings.get('credentialSecret');
            } catch(err) {
                userKey = false;
            }
            Iif (userKey === false) {
                log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
                // User has disabled encryption
                encryptionEnabled = false;
                // Check if we have a generated _credSecret to decrypt with and remove
                if (defaultKey) {
                    log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
                    if (credentialsEncrypted) {
                        try {
                            credentials = decryptCredentials(defaultKey,credentials)
                        } catch(err) {
                            credentials = {};
                            log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                        }
                    }
                    dirty = true;
                    removeDefaultKey = true;
                }
            } else Iif (typeof userKey === 'string') {
                log.debug("red/runtime/nodes/credentials.load : user provided key");
                // User has provided own encryption key, get the 32-byte hash of it
                encryptionKey = crypto.createHash('sha256').update(userKey).digest();
                encryptionEnabled = true;
 
                if (defaultKey) {
                    log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
                    // User has provided their own key, but we already have a default key
                    // Decrypt using default key
                    if (credentialsEncrypted) {
                        try {
                            credentials = decryptCredentials(defaultKey,credentials)
                        } catch(err) {
                            credentials = {};
                            log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                        }
                    }
                    dirty = true;
                    removeDefaultKey = true;
                }
            } else {
                log.debug("red/runtime/nodes/credentials.load : no user key present");
                // User has not provide their own key
                encryptionKey = defaultKey;
                encryptionEnabled = true;
                Iif (encryptionKey === undefined) {
                    log.debug("red/runtime/nodes/credentials.load : no default key present - generating one");
                    // No user-provided key, no generated key
                    // Generate a new key
                    defaultKey = crypto.randomBytes(32).toString('hex');
                    try {
                        setupEncryptionPromise = settings.set('_credentialSecret',defaultKey);
                        encryptionKey = crypto.createHash('sha256').update(defaultKey).digest();
                    } catch(err) {
                        log.debug("red/runtime/nodes/credentials.load : settings unavailable - disabling encryption");
                        // Settings unavailable
                        encryptionEnabled = false;
                        encryptionKey = null;
                    }
                    dirty = true;
                } else {
                    log.debug("red/runtime/nodes/credentials.load : using default key");
                }
            }
        }
        Eif (encryptionEnabled && !dirty) {
            encryptedCredentials = credentials;
        }
        return setupEncryptionPromise.then(function() {
            Iif (credentials.hasOwnProperty("$")) {
                // These are encrypted credentials
                try {
                    credentialCache = decryptCredentials(encryptionKey,credentials)
                } catch(err) {
                    credentialCache = {};
                    dirty = true;
                    log.warn(log._("nodes.credentials.error",{message:err.toString()}))
                }
            } else {
                credentialCache = credentials;
            }
        });
    },
 
    /**
     * Adds a set of credentials for the given node id.
     * @param id the node id for the credentials
     * @param creds an object of credential key/value pairs
     * @return a promise for backwards compatibility TODO: can this be removed?
     */
    add: function (id, creds) {
        if (!credentialCache.hasOwnProperty(id) || JSON.stringify(creds) !== JSON.stringify(credentialCache[id])) {
            credentialCache[id] = creds;
            dirty = true;
        }
        return when.resolve();
    },
 
    /**
     * Gets the credentials for the given node id.
     * @param id the node id for the credentials
     * @return the credentials
     */
    get: function (id) {
        return credentialCache[id];
    },
 
    /**
     * Deletes the credentials for the given node id.
     * @param id the node id for the credentials
     * @return a promise for the saving of credentials to storage
     */
    delete: function (id) {
        delete credentialCache[id];
        dirty = true;
    },
 
    /**
     * Deletes any credentials for nodes that no longer exist
     * @param config a flow config
     * @return a promise for the saving of credentials to storage
     */
    clean: function (config) {
        var existingIds = {};
        config.forEach(function(n) {
            existingIds[n.id] = true;
            if (n.credentials) {
                api.extract(n);
            }
        });
        var deletedCredentials = false;
        for (var c in credentialCache) {
            if (credentialCache.hasOwnProperty(c)) {
                if (!existingIds[c]) {
                    deletedCredentials = true;
                    delete credentialCache[c];
                }
            }
        }
        if (deletedCredentials) {
            dirty = true;
        }
        return when.resolve();
    },
 
    /**
     * Registers a node credential definition.
     * @param type the node type
     * @param definition the credential definition
     */
    register: function (type, definition) {
        var dashedType = type.replace(/\s+/g, '-');
        credentialsDef[dashedType] = definition;
    },
 
    /**
     * Extracts and stores any credential updates in the provided node.
     * The provided node may have a .credentials property that contains
     * new credentials for the node.
     * This function loops through the credentials in the definition for
     * the node-type and applies any of the updates provided in the node.
     *
     * This function does not save the credentials to disk as it is expected
     * to be called multiple times when a new flow is deployed.
     *
     * @param node the node to extract credentials from
     */
    extract: function(node) {
        var nodeID = node.id;
        var nodeType = node.type;
        var newCreds = node.credentials;
        if (newCreds) {
            delete node.credentials;
            var savedCredentials = credentialCache[nodeID] || {};
            var dashedType = nodeType.replace(/\s+/g, '-');
            var definition = credentialsDef[dashedType];
            if (!definition) {
                log.warn(log._("nodes.credentials.not-registered",{type:nodeType}));
                return;
            }
 
            for (var cred in definition) {
                if (definition.hasOwnProperty(cred)) {
                    if (newCreds[cred] === undefined) {
                        continue;
                    }
                    if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') {
                        continue;
                    }
                    if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) {
                        delete savedCredentials[cred];
                        dirty = true;
                        continue;
                    }
                    if (!savedCredentials.hasOwnProperty(cred) || JSON.stringify(savedCredentials[cred]) !== JSON.stringify(newCreds[cred])) {
                        savedCredentials[cred] = newCreds[cred];
                        dirty = true;
                    }
                }
            }
            credentialCache[nodeID] = savedCredentials;
        }
    },
 
    /**
     * Gets the credential definition for the given node type
     * @param type the node type
     * @return the credential definition
     */
    getDefinition: function (type) {
        return credentialsDef[type];
    },
 
    dirty: function() {
        return dirty;
    },
 
    export: function() {
        var result = credentialCache;
        if (encryptionEnabled) {
            if (dirty) {
                try {
                    log.debug("red/runtime/nodes/credentials.export : encrypting");
                    var initVector = crypto.randomBytes(16);
                    var cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, initVector);
                    result = {"$":initVector.toString('hex') + cipher.update(JSON.stringify(credentialCache), 'utf8', 'base64') + cipher.final('base64')};
                } catch(err) {
                    log.warn(log._("nodes.credentials.error-saving",{message:err.toString()}))
                }
            } else {
                result = encryptedCredentials;
            }
        }
        dirty = false;
        if (removeDefaultKey) {
            log.debug("red/runtime/nodes/credentials.export : removing unused default key");
            return settings.delete('_credentialSecret').then(function() {
                removeDefaultKey = false;
                return result;
            })
        } else {
            return when.resolve(result);
        }
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/index.js

Statements: 52.83% (28 / 53)      Branches: 31.25% (5 / 16)      Functions: 40% (2 / 5)      Lines: 52.83% (28 / 53)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163                                1 1 1   1 1 1 1 1 1 1   1   1   1                 1 51                 51 5   51                 1                                         1 1 1 1 1 1     1         1                       1                                                                                                          
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var path = require("path");
var fs = require("fs");
 
var registry = require("./registry");
var credentials = require("./credentials");
var flows = require("./flows");
var flowUtil = require("./flows/util")
var context = require("./context");
var Node = require("./Node");
var log = require("../log");
 
var events = require("../events");
 
var child_process = require('child_process');
 
var settings;
 
/**
 * Registers a node constructor
 * @param nodeSet - the nodeSet providing the node (module/set)
 * @param type - the string type name
 * @param constructor - the constructor function for this node type
 * @param opts - optional additional options for the node
 */
function registerType(nodeSet,type,constructor,opts) {
    Iif (typeof type !== "string") {
        // This is someone calling the api directly, rather than via the
        // RED object provided to a node. Log a warning
        log.warn("["+nodeSet+"] Deprecated call to RED.runtime.nodes.registerType - node-set name must be provided as first argument");
        opts = constructor;
        constructor = type;
        type = nodeSet;
        nodeSet = "";
    }
    if (opts && opts.credentials) {
        credentials.register(type,opts.credentials);
    }
    registry.registerType(nodeSet,type,constructor);
}
 
/**
 * Called from a Node's constructor function, invokes the super-class
 * constructor and attaches any credentials to the node.
 * @param node the node object being created
 * @param def the instance definition for the node
 */
function createNode(node,def) {
    Node.call(node,def);
    var id = node.id;
    if (def._alias) {
        id = def._alias;
    }
    var creds = credentials.get(id);
    if (creds) {
        //console.log("Attaching credentials to ",node.id);
        // allow $(foo) syntax to substitute env variables for credentials also...
        for (var p in creds) {
            if (creds.hasOwnProperty(p)) {
                flowUtil.mapEnvVarProperties(creds,p);
            }
        }
        node.credentials = creds;
    } else if (credentials.getDefinition(node.type)) {
        node.credentials = {};
    }
}
 
function init(runtime) {
    settings = runtime.settings;
    credentials.init(runtime);
    flows.init(runtime);
    registry.init(runtime);
    context.init(runtime.settings);
}
 
function disableNode(id) {
    flows.checkTypeInUse(id);
    return registry.disableNode(id);
}
 
function uninstallModule(module) {
    var info = registry.getModuleInfo(module);
    if (!info) {
        throw new Error(log._("nodes.index.unrecognised-module", {module:module}));
    } else {
        for (var i=0;i<info.nodes.length;i++) {
            flows.checkTypeInUse(module+"/"+info.nodes[i].name);
        }
        return registry.uninstallModule(module);
    }
}
 
module.exports = {
    // Lifecycle
    init: init,
    load: registry.load,
 
    // Node registry
    createNode: createNode,
    getNode: flows.get,
    eachNode: flows.eachNode,
 
    paletteEditorEnabled: registry.paletteEditorEnabled,
    installModule: registry.installModule,
    uninstallModule: uninstallModule,
 
    enableNode: registry.enableNode,
    disableNode: disableNode,
 
    // Node type registry
    registerType: registerType,
    getType: registry.get,
 
    getNodeInfo: registry.getNodeInfo,
    getNodeList: registry.getNodeList,
 
    getModuleInfo: registry.getModuleInfo,
 
    getNodeConfigs: registry.getNodeConfigs,
    getNodeConfig: registry.getNodeConfig,
 
    clearRegistry: registry.clear,
    cleanModuleList: registry.cleanModuleList,
 
    // Flow handling
    loadFlows:  flows.load,
    startFlows: flows.startFlows,
    stopFlows:  flows.stopFlows,
    setFlows:   flows.setFlows,
    getFlows:   flows.getFlows,
 
    addFlow:     flows.addFlow,
    getFlow:     flows.getFlow,
    updateFlow:  flows.updateFlow,
    removeFlow:  flows.removeFlow,
    // disableFlow: flows.disableFlow,
    // enableFlow:  flows.enableFlow,
 
    // Credentials
    addCredentials: credentials.add,
    getCredentials: credentials.get,
    deleteCredentials: credentials.delete,
    getCredentialDefinition: credentials.getDefinition
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/

Statements: 18.17% (153 / 842)      Branches: 3.81% (21 / 551)      Functions: 17.05% (15 / 88)      Lines: 18.3% (153 / 836)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/
File Statements Branches Functions Lines
Flow.js 13.53% (36 / 266) 2.03% (3 / 148) 16.67% (4 / 24) 13.74% (36 / 262)
index.js 26.63% (94 / 353) 8.37% (18 / 215) 19.61% (10 / 51) 26.63% (94 / 353)
util.js 10.31% (23 / 223) 0% (0 / 188) 7.69% (1 / 13) 10.41% (23 / 221)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/Flow.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/Flow.js

Statements: 13.53% (36 / 266)      Branches: 2.03% (3 / 148)      Functions: 16.67% (4 / 24)      Lines: 13.74% (36 / 262)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473                                1 1 1 1 1 1   1 1 1   1 1 1 1   1 1 1 1 1 1   1 1 1                                                             1                 1                                                       1                           1                                                                           1         1       1 1     1                                                                   1                                                                                                             1                                                       1                                                                                                                                                                                                                                                                                                                                                             1   1        
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var clone = require("clone");
var typeRegistry = require("../registry");
var Log = require("../../log");
var redUtil = require("../../util");
var flowUtil = require("./util");
 
function Flow(global,flow) {
    Eif (typeof flow === 'undefined') {
        flow = global;
    }
    var activeNodes = {};
    var subflowInstanceNodes = {};
    var catchNodeMap = {};
    var statusNodeMap = {};
 
    this.start = function(diff) {
        var node;
        var newNode;
        var id;
        catchNodeMap = {};
        statusNodeMap = {};
 
        var configNodes = Object.keys(flow.configs);
        var configNodeAttempts = {};
        while (configNodes.length > 0) {
            id = configNodes.shift();
            node = flow.configs[id];
            if (!activeNodes[id]) {
                var readyToCreate = true;
                // This node doesn't exist.
                // Check it doesn't reference another non-existent config node
                for (var prop in node) {
                    if (node.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== '_users' && flow.configs[node[prop]]) {
                        if (!activeNodes[node[prop]]) {
                            // References a non-existent config node
                            // Add it to the back of the list to try again later
                            configNodes.push(id);
                            configNodeAttempts[id] = (configNodeAttempts[id]||0)+1;
                            if (configNodeAttempts[id] === 100) {
                                throw new Error("Circular config node dependency detected: "+id);
                            }
                            readyToCreate = false;
                            break;
                        }
                    }
                }
                if (readyToCreate) {
                    newNode = createNode(node.type,node);
                    if (newNode) {
                        activeNodes[id] = newNode;
                    }
                }
            }
        }
 
        Iif (diff && diff.rewired) {
            for (var j=0;j<diff.rewired.length;j++) {
                var rewireNode = activeNodes[diff.rewired[j]];
                if (rewireNode) {
                    rewireNode.updateWires(flow.nodes[rewireNode.id].wires);
                }
            }
        }
 
        for (id in flow.nodes) {
            if (flow.nodes.hasOwnProperty(id)) {
                node = flow.nodes[id];
                if (!node.subflow) {
                    if (!activeNodes[id]) {
                        newNode = createNode(node.type,node);
                        if (newNode) {
                            activeNodes[id] = newNode;
                        }
                    }
                } else {
                    if (!subflowInstanceNodes[id]) {
                        try {
                            var nodes = createSubflow(flow.subflows[node.subflow]||global.subflows[node.subflow],node,flow.subflows,global.subflows,activeNodes);
                            subflowInstanceNodes[id] = nodes.map(function(n) { return n.id});
                            for (var i=0;i<nodes.length;i++) {
                                if (nodes[i]) {
                                    activeNodes[nodes[i].id] = nodes[i];
                                }
                            }
                        } catch(err) {
                            console.log(err.stack)
                        }
                    }
                }
            }
        }
 
        for (id in activeNodes) {
            if (activeNodes.hasOwnProperty(id)) {
                node = activeNodes[id];
                if (node.type === "catch") {
                    catchNodeMap[node.z] = catchNodeMap[node.z] || [];
                    catchNodeMap[node.z].push(node);
                } else if (node.type === "status") {
                    statusNodeMap[node.z] = statusNodeMap[node.z] || [];
                    statusNodeMap[node.z].push(node);
                }
            }
        }
    }
 
    this.stop = function(stopList) {
        return when.promise(function(resolve) {
            var i;
            if (stopList) {
                for (i=0;i<stopList.length;i++) {
                    if (subflowInstanceNodes[stopList[i]]) {
                        // The first in the list is the instance node we already
                        // know about
                        stopList = stopList.concat(subflowInstanceNodes[stopList[i]].slice(1))
                    }
                }
            } else {
                stopList = Object.keys(activeNodes);
            }
            var promises = [];
            for (i=0;i<stopList.length;i++) {
                var node = activeNodes[stopList[i]];
                if (node) {
                    delete activeNodes[stopList[i]];
                    if (subflowInstanceNodes[stopList[i]]) {
                        delete subflowInstanceNodes[stopList[i]];
                    }
                    try {
                        var p = node.close();
                        if (p) {
                            promises.push(p);
                        }
                    } catch(err) {
                        node.error(err);
                    }
                }
            }
            when.settle(promises).then(function() {
                resolve();
            });
        });
    }
 
    this.update = function(_global,_flow) {
        global = _global;
        flow = _flow;
    }
 
    this.getNode = function(id) {
        return activeNodes[id];
    }
 
    this.getActiveNodes = function() {
        return activeNodes;
    }
 
    this.handleStatus = function(node,statusMessage) {
        var targetStatusNodes = null;
        var reportingNode = node;
        var handled = false;
        while (reportingNode && !handled) {
            targetStatusNodes = statusNodeMap[reportingNode.z];
            if (targetStatusNodes) {
                targetStatusNodes.forEach(function(targetStatusNode) {
                    if (targetStatusNode.scope && targetStatusNode.scope.indexOf(node.id) === -1) {
                        return;
                    }
                    var message = {
                        status: {
                            text: "",
                            source: {
                                id: node.id,
                                type: node.type,
                                name: node.name
                            }
                        }
                    };
                    if (statusMessage.hasOwnProperty("text")) {
                        message.status.text = statusMessage.text.toString();
                    }
                    targetStatusNode.receive(message);
                    handled = true;
                });
            }
            if (!handled) {
                reportingNode = activeNodes[reportingNode.z];
            }
        }
    }
 
    this.handleError = function(node,logMessage,msg) {
        var count = 1;
        if (msg && msg.hasOwnProperty("error")) {
            if (msg.error.hasOwnProperty("source")) {
                if (msg.error.source.id === node.id) {
                    count = msg.error.source.count+1;
                    if (count === 10) {
                        node.warn(Log._("nodes.flow.error-loop"));
                        return;
                    }
                }
            }
        }
        var targetCatchNodes = null;
        var throwingNode = node;
        var handled = false;
        while (throwingNode && !handled) {
            targetCatchNodes = catchNodeMap[throwingNode.z];
            if (targetCatchNodes) {
                targetCatchNodes.forEach(function(targetCatchNode) {
                    if (targetCatchNode.scope && targetCatchNode.scope.indexOf(throwingNode.id) === -1) {
                        return;
                    }
                    var errorMessage;
                    if (msg) {
                        errorMessage = redUtil.cloneMessage(msg);
                    } else {
                        errorMessage = {};
                    }
                    if (errorMessage.hasOwnProperty("error")) {
                        errorMessage._error = errorMessage.error;
                    }
                    errorMessage.error = {
                        message: logMessage.toString(),
                        source: {
                            id: node.id,
                            type: node.type,
                            name: node.name,
                            count: count
                        }
                    };
                    if (logMessage.hasOwnProperty('stack')) {
                        errorMessage.error.stack = logMessage.stack;
                    }
                    targetCatchNode.receive(errorMessage);
                    handled = true;
                });
            }
            if (!handled) {
                throwingNode = activeNodes[throwingNode.z];
            }
        }
    }
}
 
function createNode(type,config) {
    var nn = null;
    var nt = typeRegistry.get(type);
    if (nt) {
        var conf = clone(config);
        delete conf.credentials;
        for (var p in conf) {
            if (conf.hasOwnProperty(p)) {
                flowUtil.mapEnvVarProperties(conf,p);
            }
        }
        try {
            nn = new nt(conf);
        }
        catch (err) {
            Log.log({
                level: Log.ERROR,
                id:conf.id,
                type: type,
                msg: err
            });
        }
    } else {
        Log.error(Log._("nodes.flow.unknown-type", {type:type}));
    }
    return nn;
}
 
function createSubflow(sf,sfn,subflows,globalSubflows,activeNodes) {
    //console.log("CREATE SUBFLOW",sf.id,sfn.id);
    var nodes = [];
    var node_map = {};
    var newNodes = [];
    var node;
    var wires;
    var i,j,k;
 
    var createNodeInSubflow = function(def) {
        node = clone(def);
        var nid = redUtil.generateId();
        node_map[node.id] = node;
        node._alias = node.id;
        node.id = nid;
        node.z = sfn.id;
        newNodes.push(node);
    }
 
    // Clone all of the subflow node definitions and give them new IDs
    for (i in sf.configs) {
        if (sf.configs.hasOwnProperty(i)) {
            createNodeInSubflow(sf.configs[i]);
        }
    }
    // Clone all of the subflow node definitions and give them new IDs
    for (i in sf.nodes) {
        if (sf.nodes.hasOwnProperty(i)) {
            createNodeInSubflow(sf.nodes[i]);
        }
    }
 
    // Look for any catch/status nodes and update their scope ids
    // Update all subflow interior wiring to reflect new node IDs
    for (i=0;i<newNodes.length;i++) {
        node = newNodes[i];
        if (node.wires) {
            var outputs = node.wires;
            for (j=0;j<outputs.length;j++) {
                wires = outputs[j];
                for (k=0;k<wires.length;k++) {
                    outputs[j][k] = node_map[outputs[j][k]].id
                }
            }
            if ((node.type === 'catch' || node.type === 'status') && node.scope) {
                node.scope = node.scope.map(function(id) {
                    return node_map[id]?node_map[id].id:""
                })
            } else {
                for (var prop in node) {
                    if (node.hasOwnProperty(prop) && prop !== '_alias') {
                        if (node_map[node[prop]]) {
                            //console.log("Mapped",node.type,node.id,prop,node_map[node[prop]].id);
                            node[prop] = node_map[node[prop]].id;
                        }
                    }
                }
            }
        }
    }
 
    // Create a subflow node to accept inbound messages and route appropriately
    var Node = require("../Node");
    var subflowInstance = {
        id: sfn.id,
        type: sfn.type,
        z: sfn.z,
        name: sfn.name,
        wires: []
    }
    if (sf.in) {
        subflowInstance.wires = sf.in.map(function(n) { return n.wires.map(function(w) { return node_map[w.id].id;})})
        subflowInstance._originalWires = clone(subflowInstance.wires);
    }
    var subflowNode = new Node(subflowInstance);
 
    subflowNode.on("input", function(msg) { this.send(msg);});
 
 
    subflowNode._updateWires = subflowNode.updateWires;
 
    subflowNode.updateWires = function(newWires) {
        // Wire the subflow outputs
        if (sf.out) {
            var node,wires,i,j;
            // Restore the original wiring to the internal nodes
            subflowInstance.wires = clone(subflowInstance._originalWires);
            for (i=0;i<sf.out.length;i++) {
                wires = sf.out[i].wires;
                for (j=0;j<wires.length;j++) {
                    if (wires[j].id != sf.id) {
                        node = node_map[wires[j].id];
                        if (node._originalWires) {
                            node.wires = clone(node._originalWires);
                        }
                    }
                }
            }
 
            var modifiedNodes = {};
            var subflowInstanceModified = false;
 
            for (i=0;i<sf.out.length;i++) {
                wires = sf.out[i].wires;
                for (j=0;j<wires.length;j++) {
                    if (wires[j].id === sf.id) {
                        subflowInstance.wires[wires[j].port] = subflowInstance.wires[wires[j].port].concat(newWires[i]);
                        subflowInstanceModified = true;
                    } else {
                        node = node_map[wires[j].id];
                        node.wires[wires[j].port] = node.wires[wires[j].port].concat(newWires[i]);
                        modifiedNodes[node.id] = node;
                    }
                }
            }
            Object.keys(modifiedNodes).forEach(function(id) {
                var node = modifiedNodes[id];
                subflowNode.instanceNodes[id].updateWires(node.wires);
            });
            if (subflowInstanceModified) {
                subflowNode._updateWires(subflowInstance.wires);
            }
        }
    }
 
    nodes.push(subflowNode);
 
    // Wire the subflow outputs
    if (sf.out) {
        var modifiedNodes = {};
        for (i=0;i<sf.out.length;i++) {
            wires = sf.out[i].wires;
            for (j=0;j<wires.length;j++) {
                if (wires[j].id === sf.id) {
                    // A subflow input wired straight to a subflow output
                    subflowInstance.wires[wires[j].port] = subflowInstance.wires[wires[j].port].concat(sfn.wires[i])
                    subflowNode._updateWires(subflowInstance.wires);
                } else {
                    node = node_map[wires[j].id];
                    modifiedNodes[node.id] = node;
                    if (!node._originalWires) {
                        node._originalWires = clone(node.wires);
                    }
                    node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(sfn.wires[i]);
                }
            }
        }
    }
 
    // Instantiate the nodes
    for (i=0;i<newNodes.length;i++) {
        node = newNodes[i];
        var type = node.type;
 
        var m = /^subflow:(.+)$/.exec(type);
        if (!m) {
            var newNode = createNode(type,node);
            if (newNode) {
                activeNodes[node.id] = newNode;
                nodes.push(newNode);
            }
        } else {
            var subflowId = m[1];
            nodes = nodes.concat(createSubflow(subflows[subflowId]||globalSubflows[subflowId],node,subflows,globalSubflows,activeNodes));
        }
    }
 
    subflowNode.instanceNodes = {};
 
    nodes.forEach(function(node) {
        subflowNode.instanceNodes[node.id] = node;
    });
    return nodes;
}
 
module.exports = {
    create: function(global,conf) {
        return new Flow(global,conf);
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/index.js

Statements: 26.63% (94 / 353)      Branches: 8.37% (18 / 215)      Functions: 19.61% (10 / 51)      Lines: 26.63% (94 / 353)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650                                1 1   1   1 1 1   1 1 1 1 1   1 1   1 1   1 1   1 1   1   1 1     1 1 1 1 1 51                       1       1 1 1 1 1             1 1               1 1   1 1 1 1 1 1 1 1 1 1 1 1                                       1   1     1       1 1                     1                               1               1       1                     1                         1             1                                   1   1 1 1 1                                           1 1     1     1 1 1 1   1                                       1 1 1 1 1                   1 1   1 1     1     1     1                                                                                                                   1                                                 1                                     1                                                                                               1                                                                                                                               1                                                                                                         1                                           1                                                                                                        
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var clone = require("clone");
var when = require("when");
 
var Flow = require('./Flow');
 
var typeRegistry = require("../registry");
var context = require("../context")
var credentials = require("../credentials");
 
var flowUtil = require("./util");
var log = require("../../log");
var events = require("../../events");
var redUtil = require("../../util");
var deprecated = require("../registry/deprecated");
 
var storage = null;
var settings = null;
 
var activeConfig = null;
var activeFlowConfig = null;
 
var activeFlows = {};
var started = false;
 
var activeNodesToFlow = {};
var subflowInstanceNodeMap = {};
 
var typeEventRegistered = false;
 
function init(runtime) {
    Iif (started) {
        throw new Error("Cannot init without a stop");
    }
    settings = runtime.settings;
    storage = runtime.storage;
    started = false;
    Eif (!typeEventRegistered) {
        events.on('type-registered',function(type) {
            Iif (activeFlowConfig && activeFlowConfig.missingTypes.length > 0) {
                var i = activeFlowConfig.missingTypes.indexOf(type);
                if (i != -1) {
                    log.info(log._("nodes.flows.registered-missing", {type:type}));
                    activeFlowConfig.missingTypes.splice(i,1);
                    if (activeFlowConfig.missingTypes.length === 0 && started) {
                        events.emit("runtime-event",{id:"runtime-state"});
                        start();
                    }
                }
            }
        });
        typeEventRegistered = true;
    }
}
 
function loadFlows() {
    return storage.getFlows().then(function(config) {
        log.debug("loaded flow revision: "+config.rev);
        return credentials.load(config.credentials).then(function() {
            return config;
        });
    }).otherwise(function(err) {
        log.warn(log._("nodes.flows.error",{message:err.toString()}));
        console.log(err.stack);
    });
}
function load() {
    return setFlows(null,"load",false);
}
 
/*
 * _config - new node array configuration
 * type - full/nodes/flows/load (default full)
 * muteLog - don't emit the standard log messages (used for individual flow api)
 */
function setFlows(_config,type,muteLog) {
    type = type||"full";
 
    var configSavePromise = null;
    var config = null;
    var diff;
    var newFlowConfig;
    var isLoad = false;
    Eif (type === "load") {
        isLoad = true;
        configSavePromise = loadFlows().then(function(_config) {
            config = clone(_config.flows);
            newFlowConfig = flowUtil.parseConfig(clone(config));
            type = "full";
            return _config.rev;
        });
    } else {
        config = clone(_config);
        newFlowConfig = flowUtil.parseConfig(clone(config));
        if (type !== 'full') {
            diff = flowUtil.diffConfigs(activeFlowConfig,newFlowConfig);
        }
        credentials.clean(config);
        var credsDirty = credentials.dirty();
        configSavePromise = credentials.export().then(function(creds) {
            var saveConfig = {
                flows: config,
                credentialsDirty:credsDirty,
                credentials: creds
            }
            return storage.saveFlows(saveConfig);
        });
    }
 
    return configSavePromise
        .then(function(flowRevision) {
            Iif (!isLoad) {
                log.debug("saved flow revision: "+flowRevision);
            }
            activeConfig = {
                flows:config,
                rev:flowRevision
            };
            activeFlowConfig = newFlowConfig;
            Iif (started) {
                return stop(type,diff,muteLog).then(function() {
                    context.clean(activeFlowConfig);
                    start(type,diff,muteLog);
                    return flowRevision;
                }).otherwise(function(err) {
                })
            }
        });
}
 
function getNode(id) {
    var node;
    if (activeNodesToFlow[id] && activeFlows[activeNodesToFlow[id]]) {
        return activeFlows[activeNodesToFlow[id]].getNode(id);
    }
    for (var flowId in activeFlows) {
        if (activeFlows.hasOwnProperty(flowId)) {
            node = activeFlows[flowId].getNode(id);
            if (node) {
                return node;
            }
        }
    }
    return null;
}
 
function eachNode(cb) {
    for (var id in activeFlowConfig.allNodes) {
        if (activeFlowConfig.allNodes.hasOwnProperty(id)) {
            cb(activeFlowConfig.allNodes[id]);
        }
    }
}
 
function getFlows() {
    return activeConfig;
}
 
function delegateError(node,logMessage,msg) {
    if (activeFlows[node.z]) {
        activeFlows[node.z].handleError(node,logMessage,msg);
    } else if (activeNodesToFlow[node.z] && activeFlows[activeNodesToFlow[node.z]]) {
        activeFlows[activeNodesToFlow[node.z]].handleError(node,logMessage,msg);
    } else if (activeFlowConfig.subflows[node.z] && subflowInstanceNodeMap[node.id]) {
        subflowInstanceNodeMap[node.id].forEach(function(n) {
            delegateError(getNode(n),logMessage,msg);
        });
    }
}
function handleError(node,logMessage,msg) {
    if (node.z) {
        delegateError(node,logMessage,msg);
    } else {
        if (activeFlowConfig.configs[node.id]) {
            activeFlowConfig.configs[node.id]._users.forEach(function(id) {
                var userNode = activeFlowConfig.allNodes[id];
                delegateError(userNode,logMessage,msg);
            })
        }
    }
}
 
function delegateStatus(node,statusMessage) {
    if (activeFlows[node.z]) {
        activeFlows[node.z].handleStatus(node,statusMessage);
    } else if (activeNodesToFlow[node.z] && activeFlows[activeNodesToFlow[node.z]]) {
        activeFlows[activeNodesToFlow[node.z]].handleStatus(node,statusMessage);
    }
}
function handleStatus(node,statusMessage) {
    events.emit("node-status",{
        id: node.id,
        status:statusMessage
    });
    if (node.z) {
        delegateStatus(node,statusMessage);
    } else {
        if (activeFlowConfig.configs[node.id]) {
            activeFlowConfig.configs[node.id]._users.forEach(function(id) {
                var userNode = activeFlowConfig.allNodes[id];
                delegateStatus(userNode,statusMessage);
            })
        }
    }
}
 
 
function start(type,diff,muteLog) {
    //dumpActiveNodes();
    type = type||"full";
    started = true;
    var i;
    Iif (activeFlowConfig.missingTypes.length > 0) {
        log.info(log._("nodes.flows.missing-types"));
        var knownUnknowns = 0;
        for (i=0;i<activeFlowConfig.missingTypes.length;i++) {
            var nodeType = activeFlowConfig.missingTypes[i];
            var info = deprecated.get(nodeType);
            if (info) {
                log.info(log._("nodes.flows.missing-type-provided",{type:activeFlowConfig.missingTypes[i],module:info.module}));
                knownUnknowns += 1;
            } else {
                log.info(" - "+activeFlowConfig.missingTypes[i]);
            }
        }
        if (knownUnknowns > 0) {
            log.info(log._("nodes.flows.missing-type-install-1"));
            log.info("  npm install <module name>");
            log.info(log._("nodes.flows.missing-type-install-2"));
            log.info("  "+settings.userDir);
        }
        events.emit("runtime-event",{id:"runtime-state",type:"warning",text:"notification.warnings.missing-types"});
        return when.resolve();
    }
    Eif (!muteLog) {
        Iif (diff) {
            log.info(log._("nodes.flows.starting-modified-"+type));
        } else {
            log.info(log._("nodes.flows.starting-flows"));
        }
    }
    var id;
    Eif (!diff) {
        Eif (!activeFlows['global']) {
            activeFlows['global'] = Flow.create(activeFlowConfig);
        }
        for (id in activeFlowConfig.flows) {
            if (activeFlowConfig.flows.hasOwnProperty(id)) {
                if (!activeFlows[id]) {
                    activeFlows[id] = Flow.create(activeFlowConfig,activeFlowConfig.flows[id]);
                }
            }
        }
    } else {
        activeFlows['global'].update(activeFlowConfig,activeFlowConfig);
        for (id in activeFlowConfig.flows) {
            if (activeFlowConfig.flows.hasOwnProperty(id)) {
                if (activeFlows[id]) {
                    activeFlows[id].update(activeFlowConfig,activeFlowConfig.flows[id]);
                } else {
                    activeFlows[id] = Flow.create(activeFlowConfig,activeFlowConfig.flows[id]);
                }
            }
        }
    }
 
    for (id in activeFlows) {
        Eif (activeFlows.hasOwnProperty(id)) {
            activeFlows[id].start(diff);
            var activeNodes = activeFlows[id].getActiveNodes();
            Object.keys(activeNodes).forEach(function(nid) {
                activeNodesToFlow[nid] = id;
                if (activeNodes[nid]._alias) {
                    subflowInstanceNodeMap[activeNodes[nid]._alias] = subflowInstanceNodeMap[activeNodes[nid]._alias] || [];
                    subflowInstanceNodeMap[activeNodes[nid]._alias].push(nid);
                }
            });
 
        }
    }
    events.emit("nodes-started");
    events.emit("runtime-event",{id:"runtime-state"});
 
    Eif (!muteLog) {
        Iif (diff) {
            log.info(log._("nodes.flows.started-modified-"+type));
        } else {
            log.info(log._("nodes.flows.started-flows"));
        }
    }
    return when.resolve();
}
 
function stop(type,diff,muteLog) {
    type = type||"full";
    if (!muteLog) {
        if (diff) {
            log.info(log._("nodes.flows.stopping-modified-"+type));
        } else {
            log.info(log._("nodes.flows.stopping-flows"));
        }
    }
    started = false;
    var promises = [];
    var stopList;
    if (type === 'nodes') {
        stopList = diff.changed.concat(diff.removed);
    } else if (type === 'flows') {
        stopList = diff.changed.concat(diff.removed).concat(diff.linked);
    }
    for (var id in activeFlows) {
        if (activeFlows.hasOwnProperty(id)) {
            promises = promises.concat(activeFlows[id].stop(stopList));
            if (!diff || diff.removed.indexOf(id)!==-1) {
                delete activeFlows[id];
            }
        }
    }
 
    return when.promise(function(resolve,reject) {
        when.settle(promises).then(function() {
            for (id in activeNodesToFlow) {
                if (activeNodesToFlow.hasOwnProperty(id)) {
                    if (!activeFlows[activeNodesToFlow[id]]) {
                        delete activeNodesToFlow[id];
                    }
                }
            }
            if (stopList) {
                stopList.forEach(function(id) {
                    delete activeNodesToFlow[id];
                });
            }
            // Ideally we'd prune just what got stopped - but mapping stopList
            // id to the list of subflow instance nodes is something only Flow
            // can do... so cheat by wiping the map knowing it'll be rebuilt
            // in start()
            subflowInstanceNodeMap = {};
            if (!muteLog) {
                if (diff) {
                    log.info(log._("nodes.flows.stopped-modified-"+type));
                } else {
                    log.info(log._("nodes.flows.stopped-flows"));
                }
            }
            resolve();
        });
    });
}
 
 
function checkTypeInUse(id) {
    var nodeInfo = typeRegistry.getNodeInfo(id);
    if (!nodeInfo) {
        throw new Error(log._("nodes.index.unrecognised-id", {id:id}));
    } else {
        var inUse = {};
        var config = getFlows();
        config.flows.forEach(function(n) {
            inUse[n.type] = (inUse[n.type]||0)+1;
        });
        var nodesInUse = [];
        nodeInfo.types.forEach(function(t) {
            if (inUse[t]) {
                nodesInUse.push(t);
            }
        });
        if (nodesInUse.length > 0) {
            var msg = nodesInUse.join(", ");
            var err = new Error(log._("nodes.index.type-in-use", {msg:msg}));
            err.code = "type_in_use";
            throw err;
        }
    }
}
 
function updateMissingTypes() {
    var subflowInstanceRE = /^subflow:(.+)$/;
    activeFlowConfig.missingTypes = [];
 
    for (var id in activeFlowConfig.allNodes) {
        if (activeFlowConfig.allNodes.hasOwnProperty(id)) {
            var node = activeFlowConfig.allNodes[id];
            if (node.type !== 'tab' && node.type !== 'subflow') {
                var subflowDetails = subflowInstanceRE.exec(node.type);
                if ( (subflowDetails && !activeFlowConfig.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(node.type)) ) {
                    if (activeFlowConfig.missingTypes.indexOf(node.type) === -1) {
                        activeFlowConfig.missingTypes.push(node.type);
                    }
                }
            }
        }
    }
}
 
function addFlow(flow) {
    var i,node;
    if (!flow.hasOwnProperty('nodes')) {
        throw new Error('missing nodes property');
    }
    flow.id = redUtil.generateId();
 
    var nodes = [{
        type:'tab',
        label:flow.label,
        id:flow.id
    }];
 
    for (i=0;i<flow.nodes.length;i++) {
        node = flow.nodes[i];
        if (activeFlowConfig.allNodes[node.id]) {
            // TODO nls
            return when.reject(new Error('duplicate id'));
        }
        if (node.type === 'tab' || node.type === 'subflow') {
            return when.reject(new Error('invalid node type: '+node.type));
        }
        node.z = flow.id;
        nodes.push(node);
    }
    if (flow.configs) {
        for (i=0;i<flow.configs.length;i++) {
            node = flow.configs[i];
            if (activeFlowConfig.allNodes[node.id]) {
                // TODO nls
                return when.reject(new Error('duplicate id'));
            }
            if (node.type === 'tab' || node.type === 'subflow') {
                return when.reject(new Error('invalid node type: '+node.type));
            }
            node.z = flow.id;
            nodes.push(node);
        }
    }
    var newConfig = clone(activeConfig.flows);
    newConfig = newConfig.concat(nodes);
 
    return setFlows(newConfig,'flows',true).then(function() {
        log.info(log._("nodes.flows.added-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
        return flow.id;
    });
}
 
function getFlow(id) {
    var flow;
    if (id === 'global') {
        flow = activeFlowConfig;
    } else {
        flow = activeFlowConfig.flows[id];
    }
    if (!flow) {
        return null;
    }
    var result = {
        id: id
    };
    if (flow.label) {
        result.label = flow.label;
    }
    if (id !== 'global') {
        result.nodes = [];
    }
    if (flow.nodes) {
        var nodeIds = Object.keys(flow.nodes);
        if (nodeIds.length > 0) {
            result.nodes = nodeIds.map(function(nodeId) {
                var node = clone(flow.nodes[nodeId]);
                if (node.type === 'link out') {
                    delete node.wires;
                }
                return node;
            })
        }
    }
    if (flow.configs) {
        var configIds = Object.keys(flow.configs);
        result.configs = configIds.map(function(configId) {
            return clone(flow.configs[configId]);
        })
        if (result.configs.length === 0) {
            delete result.configs;
        }
    }
    if (flow.subflows) {
        var subflowIds = Object.keys(flow.subflows);
        result.subflows = subflowIds.map(function(subflowId) {
            var subflow = clone(flow.subflows[subflowId]);
            var nodeIds = Object.keys(subflow.nodes);
            subflow.nodes = nodeIds.map(function(id) {
                return subflow.nodes[id];
            });
            if (subflow.configs) {
                var configIds = Object.keys(subflow.configs);
                subflow.configs = configIds.map(function(id) {
                    return subflow.configs[id];
                })
            }
            delete subflow.instances;
            return subflow;
        });
        if (result.subflows.length === 0) {
            delete result.subflows;
        }
    }
    return result;
}
 
function updateFlow(id,newFlow) {
    var label = id;
    if (id !== 'global') {
        if (!activeFlowConfig.flows[id]) {
            var e = new Error();
            e.code = 404;
            throw e;
        }
        label = activeFlowConfig.flows[id].label;
    }
    var newConfig = clone(activeConfig.flows);
    var nodes;
 
    if (id === 'global') {
        // Remove all nodes whose z is not a known flow
        // When subflows can be owned by a flow, this logic will have to take
        // that into account
        newConfig = newConfig.filter(function(node) {
            return node.type === 'tab' || (node.hasOwnProperty('z') && activeFlowConfig.flows.hasOwnProperty(node.z));
        })
 
        // Add in the new config nodes
        nodes = newFlow.configs||[];
        if (newFlow.subflows) {
            // Add in the new subflows
            newFlow.subflows.forEach(function(sf) {
                nodes = nodes.concat(sf.nodes||[]).concat(sf.configs||[]);
                delete sf.nodes;
                delete sf.configs;
                nodes.push(sf);
            });
        }
    } else {
        newConfig = newConfig.filter(function(node) {
            return node.z !== id && node.id !== id;
        });
        var tabNode = {
            type:'tab',
            label:newFlow.label,
            id:id
        }
        nodes = [tabNode].concat(newFlow.nodes||[]).concat(newFlow.configs||[]);
        nodes.forEach(function(n) {
            n.z = id;
        });
    }
 
    newConfig = newConfig.concat(nodes);
    return setFlows(newConfig,'flows',true).then(function() {
        log.info(log._("nodes.flows.updated-flow",{label:(label?label+" ":"")+"["+id+"]"}));
    })
}
 
function removeFlow(id) {
    if (id === 'global') {
        // TODO: nls + error code
        throw new Error('not allowed to remove global');
    }
    var flow = activeFlowConfig.flows[id];
    if (!flow) {
        var e = new Error();
        e.code = 404;
        throw e;
    }
 
    var newConfig = clone(activeConfig.flows);
    newConfig = newConfig.filter(function(node) {
        return node.z !== id && node.id !== id;
    });
 
    return setFlows(newConfig,'flows',true).then(function() {
        log.info(log._("nodes.flows.removed-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
    });
}
 
module.exports = {
    init: init,
 
    /**
     * Load the current flow configuration from storage
     * @return a promise for the loading of the config
     */
    load: load,
 
    get:getNode,
    eachNode: eachNode,
 
    /**
     * Gets the current flow configuration
     */
    getFlows: getFlows,
 
    /**
     * Sets the current active config.
     * @param config the configuration to enable
     * @param type the type of deployment to do: full (default), nodes, flows, load
     * @return a promise for the saving/starting of the new flow
     */
    setFlows: setFlows,
 
    /**
     * Starts the current flow configuration
     */
    startFlows: start,
 
    /**
     * Stops the current flow configuration
     * @return a promise for the stopping of the flow
     */
    stopFlows: stop,
 
    get started() { return started },
 
    handleError: handleError,
    handleStatus: handleStatus,
 
    checkTypeInUse: checkTypeInUse,
 
    addFlow: addFlow,
    getFlow: getFlow,
    updateFlow: updateFlow,
    removeFlow: removeFlow,
    disableFlow:null,
    enableFlow:null
 
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/util.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/flows/util.js

Statements: 10.31% (23 / 223)      Branches: 0% (0 / 188)      Functions: 7.69% (1 / 13)      Lines: 10.41% (23 / 221)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412                              1 1 1 1   1                                     1   1                                             1           1 1 1 1 1 1   1                   1               1 1 1                                                                                             1             1 1                                                     1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
var clone = require("clone");
var redUtil = require("../../util");
var subflowInstanceRE = /^subflow:(.+)$/;
var typeRegistry = require("../registry");
 
function diffNodes(oldNode,newNode) {
    if (oldNode == null) {
        return true;
    }
    var oldKeys = Object.keys(oldNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" });
    var newKeys = Object.keys(newNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" });
    if (oldKeys.length != newKeys.length) {
        return true;
    }
    for (var i=0;i<newKeys.length;i++) {
        var p = newKeys[i];
        if (!redUtil.compareObjects(oldNode[p],newNode[p])) {
            return true;
        }
    }
 
    return false;
}
 
var EnvVarPropertyRE = /^\$\((\S+)\)$/;
 
function mapEnvVarProperties(obj,prop) {
    if (Buffer.isBuffer(obj[prop])) {
        return;
    } else if (Array.isArray(obj[prop])) {
        for (var i=0;i<obj[prop].length;i++) {
            mapEnvVarProperties(obj[prop],i);
        }
    } else if (typeof obj[prop] === 'string') {
        var m;
        if ( (m = EnvVarPropertyRE.exec(obj[prop])) !== null) {
            if (process.env.hasOwnProperty(m[1])) {
                obj[prop] = process.env[m[1]];
            }
        }
    } else {
        for (var p in obj[prop]) {
            if (obj[prop].hasOwnProperty(p)) {
                mapEnvVarProperties(obj[prop],p);
            }
        }
    }
}
 
module.exports = {
 
    diffNodes: diffNodes,
    mapEnvVarProperties: mapEnvVarProperties,
 
    parseConfig: function(config) {
        var flow = {};
        flow.allNodes = {};
        flow.subflows = {};
        flow.configs = {};
        flow.flows = {};
        flow.missingTypes = [];
 
        config.forEach(function(n) {
            flow.allNodes[n.id] = clone(n);
            if (n.type === 'tab') {
                flow.flows[n.id] = n;
                flow.flows[n.id].subflows = {};
                flow.flows[n.id].configs = {};
                flow.flows[n.id].nodes = {};
            }
        });
 
        config.forEach(function(n) {
            if (n.type === 'subflow') {
                flow.subflows[n.id] = n;
                flow.subflows[n.id].configs = {};
                flow.subflows[n.id].nodes = {};
                flow.subflows[n.id].instances = [];
            }
        });
        var linkWires = {};
        var linkOutNodes = [];
        config.forEach(function(n) {
            if (n.type !== 'subflow' && n.type !== 'tab') {
                var subflowDetails = subflowInstanceRE.exec(n.type);
 
                if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
                    if (flow.missingTypes.indexOf(n.type) === -1) {
                        flow.missingTypes.push(n.type);
                    }
                }
                var container = null;
                if (flow.flows[n.z]) {
                    container = flow.flows[n.z];
                } else if (flow.subflows[n.z]) {
                    container = flow.subflows[n.z];
                }
                if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
                    if (subflowDetails) {
                        var subflowType = subflowDetails[1]
                        n.subflow = subflowType;
                        flow.subflows[subflowType].instances.push(n)
                    }
                    if (container) {
                        container.nodes[n.id] = n;
                    }
                } else {
                    if (container) {
                        container.configs[n.id] = n;
                    } else {
                        flow.configs[n.id] = n;
                        flow.configs[n.id]._users = [];
                    }
                }
                if (n.type === 'link in' && n.links) {
                    // Ensure wires are present in corresponding link out nodes
                    n.links.forEach(function(id) {
                        linkWires[id] = linkWires[id]||{};
                        linkWires[id][n.id] = true;
                    })
                } else if (n.type === 'link out' && n.links) {
                    linkWires[n.id] = linkWires[n.id]||{};
                    n.links.forEach(function(id) {
                        linkWires[n.id][id] = true;
                    })
                    linkOutNodes.push(n);
                }
            }
        });
        linkOutNodes.forEach(function(n) {
            var links = linkWires[n.id];
            var targets = Object.keys(links);
            n.wires = [targets];
        });
 
 
        var addedTabs = {};
        config.forEach(function(n) {
            if (n.type !== 'subflow' && n.type !== 'tab') {
                for (var prop in n) {
                    if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
                        // This property references a global config node
                        flow.configs[n[prop]]._users.push(n.id)
                    }
                }
                if (n.z && !flow.subflows[n.z]) {
 
                    if (!flow.flows[n.z]) {
                        flow.flows[n.z] = {type:'tab',id:n.z};
                        flow.flows[n.z].subflows = {};
                        flow.flows[n.z].configs = {};
                        flow.flows[n.z].nodes = {};
                        addedTabs[n.z] = flow.flows[n.z];
                    }
                    if (addedTabs[n.z]) {
                        if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
                            addedTabs[n.z].nodes[n.id] = n;
                        } else {
                            addedTabs[n.z].configs[n.id] = n;
                        }
                    }
                }
            }
        });
        return flow;
    },
 
    diffConfigs: function(oldConfig, newConfig) {
        var id;
        var node;
        var nn;
        var wires;
        var j,k;
 
        var changedSubflows = {};
 
        var added = {};
        var removed = {};
        var changed = {};
        var wiringChanged = {};
 
        var linkMap = {};
 
        for (id in oldConfig.allNodes) {
            if (oldConfig.allNodes.hasOwnProperty(id)) {
                node = oldConfig.allNodes[id];
                // build the map of what this node was previously wired to
                if (node.wires) {
                    linkMap[node.id] = linkMap[node.id] || [];
                    for (j=0;j<node.wires.length;j++) {
                        wires = node.wires[j];
                        for (k=0;k<wires.length;k++) {
                            linkMap[node.id].push(wires[k]);
                            nn = oldConfig.allNodes[wires[k]];
                            if (nn) {
                                linkMap[nn.id] = linkMap[nn.id] || [];
                                linkMap[nn.id].push(node.id);
                            }
                        }
                    }
                }
                // This node has been removed
                if (!newConfig.allNodes.hasOwnProperty(id)) {
                    removed[id] = node;
                    // Mark the container as changed
                    if (newConfig.allNodes[removed[id].z]) {
                        changed[removed[id].z] = newConfig.allNodes[removed[id].z];
                        if (changed[removed[id].z].type === "subflow") {
                            changedSubflows[removed[id].z] = changed[removed[id].z];
                            //delete removed[id];
                        }
                    }
                } else {
                    // This node has a material configuration change
                    if (diffNodes(node,newConfig.allNodes[id]) || newConfig.allNodes[id].credentials) {
                        changed[id] = newConfig.allNodes[id];
                        if (changed[id].type === "subflow") {
                            changedSubflows[id] = changed[id];
                        }
                        // Mark the container as changed
                        if (newConfig.allNodes[changed[id].z]) {
                            changed[changed[id].z] = newConfig.allNodes[changed[id].z];
                            if (changed[changed[id].z].type === "subflow") {
                                changedSubflows[changed[id].z] = changed[changed[id].z];
                                delete changed[id];
                            }
                        }
                    }
                    // This node's wiring has changed
                    if (!redUtil.compareObjects(node.wires,newConfig.allNodes[id].wires)) {
                        wiringChanged[id] = newConfig.allNodes[id];
                        // Mark the container as changed
                        if (newConfig.allNodes[wiringChanged[id].z]) {
                            changed[wiringChanged[id].z] = newConfig.allNodes[wiringChanged[id].z];
                            if (changed[wiringChanged[id].z].type === "subflow") {
                                changedSubflows[wiringChanged[id].z] = changed[wiringChanged[id].z];
                                delete wiringChanged[id];
                            }
                        }
                    }
                }
            }
        }
        // Look for added nodes
        for (id in newConfig.allNodes) {
            if (newConfig.allNodes.hasOwnProperty(id)) {
                node = newConfig.allNodes[id];
                // build the map of what this node is now wired to
                if (node.wires) {
                    linkMap[node.id] = linkMap[node.id] || [];
                    for (j=0;j<node.wires.length;j++) {
                        wires = node.wires[j];
                        for (k=0;k<wires.length;k++) {
                            if (linkMap[node.id].indexOf(wires[k]) === -1) {
                                linkMap[node.id].push(wires[k]);
                            }
                            nn = newConfig.allNodes[wires[k]];
                            if (nn) {
                                linkMap[nn.id] = linkMap[nn.id] || [];
                                if (linkMap[nn.id].indexOf(node.id) === -1) {
                                    linkMap[nn.id].push(node.id);
                                }
                            }
                        }
                    }
                }
                // This node has been added
                if (!oldConfig.allNodes.hasOwnProperty(id)) {
                    added[id] = node;
                    // Mark the container as changed
                    if (newConfig.allNodes[added[id].z]) {
                        changed[added[id].z] = newConfig.allNodes[added[id].z];
                        if (changed[added[id].z].type === "subflow") {
                            changedSubflows[added[id].z] = changed[added[id].z];
                            delete added[id];
                        }
                    }
                }
            }
        }
 
        var madeChange;
        // Loop through the nodes looking for references to changed config nodes
        // Repeat the loop if anything is marked as changed as it may need to be
        // propagated to parent nodes.
        // TODO: looping through all nodes every time is a bit inefficient - could be more targeted
        do {
            madeChange = false;
            for (id in newConfig.allNodes) {
                if (newConfig.allNodes.hasOwnProperty(id)) {
                    node = newConfig.allNodes[id];
                    for (var prop in node) {
                        if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") {
                            // This node has a property that references a changed/removed node
                            // Assume it is a config node change and mark this node as
                            // changed.
                            if (changed[node[prop]] || removed[node[prop]]) {
                                if (!changed[node.id]) {
                                    madeChange = true;
                                    changed[node.id] = node;
                                    // This node exists within subflow template
                                    // Mark the template as having changed
                                    if (newConfig.allNodes[node.z]) {
                                        changed[node.z] = newConfig.allNodes[node.z];
                                        if (changed[node.z].type === "subflow") {
                                            changedSubflows[node.z] = changed[node.z];
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } while (madeChange===true)
 
        // Find any nodes that exist on a subflow template and remove from changed
        // list as the parent subflow will now be marked as containing a change
        for (id in newConfig.allNodes) {
            if (newConfig.allNodes.hasOwnProperty(id)) {
                node = newConfig.allNodes[id];
                if (newConfig.allNodes[node.z] && newConfig.allNodes[node.z].type === "subflow") {
                    delete changed[node.id];
                }
            }
        }
 
        // Recursively mark all instances of changed subflows as changed
        var changedSubflowStack = Object.keys(changedSubflows);
        while (changedSubflowStack.length > 0) {
            var subflowId = changedSubflowStack.pop();
            for (id in newConfig.allNodes) {
                if (newConfig.allNodes.hasOwnProperty(id)) {
                    node = newConfig.allNodes[id];
                    if (node.type === 'subflow:'+subflowId) {
                        if (!changed[node.id]) {
                            changed[node.id] = node;
                            if (!changed[changed[node.id].z] && newConfig.allNodes[changed[node.id].z]) {
                                changed[changed[node.id].z] = newConfig.allNodes[changed[node.id].z];
                                if (newConfig.allNodes[changed[node.id].z].type === "subflow") {
                                    // This subflow instance is inside a subflow. Add the
                                    // containing subflow to the stack to mark
                                    changedSubflowStack.push(changed[node.id].z);
                                    delete changed[node.id];
                                }
                            }
                        }
                    }
                }
            }
        }
 
        var diff = {
            added:Object.keys(added),
            changed:Object.keys(changed),
            removed:Object.keys(removed),
            rewired:Object.keys(wiringChanged),
            linked:[]
        }
 
        // Traverse the links of all modified nodes to mark the connected nodes
        var modifiedNodes = diff.added.concat(diff.changed).concat(diff.removed).concat(diff.rewired);
        var visited = {};
        while (modifiedNodes.length > 0) {
            node = modifiedNodes.pop();
            if (!visited[node]) {
                visited[node] = true;
                if (linkMap[node]) {
                    if (!changed[node] && !added[node] && !removed[node] && !wiringChanged[node]) {
                        diff.linked.push(node);
                    }
                    modifiedNodes = modifiedNodes.concat(linkMap[node]);
                }
            }
        }
        // for (id in newConfig.allNodes) {
        //     console.log(
        //         (added[id]?"+":(changed[id]?"!":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"~":" "),
        //         id,
        //         newConfig.allNodes[id].type,
        //         newConfig.allNodes[id].name||newConfig.allNodes[id].label||""
        //     );
        // }
        // for (id in removed) {
        //     console.log(
        //         "- "+(diff.linked.indexOf(id)!==-1?"~":" "),
        //         id,
        //         oldConfig.allNodes[id].type,
        //         oldConfig.allNodes[id].name||oldConfig.allNodes[id].label||""
        //     );
        // }
 
        return diff;
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/

Statements: 57.23% (459 / 802)      Branches: 42.34% (141 / 333)      Functions: 54.64% (53 / 97)      Lines: 57.38% (459 / 800)      Ignored: 13 branches     

All files » node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/
File Statements Branches Functions Lines
deprecated.js 66.67% (2 / 3) 100% (0 / 0) 0% (0 / 1) 66.67% (2 / 3)
index.js 70.37% (19 / 27) 0% (0 / 2) 28.57% (2 / 7) 70.37% (19 / 27)
installer.js 25% (28 / 112) 11.9% (5 / 42) 26.67% (4 / 15) 25% (28 / 112)
loader.js 72.99% (154 / 211) 55.95% (47 / 84) 70.37% (19 / 27) 72.99% (154 / 211)
localfilesystem.js 79.08% (121 / 153) 53.85% (35 / 65) 75% (12 / 16) 79.61% (121 / 152)
registry.js 45.61% (135 / 296) 38.57% (54 / 140) 51.61% (16 / 31) 45.76% (135 / 295)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/deprecated.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/deprecated.js

Statements: 66.67% (2 / 3)      Branches: 100% (0 / 0)      Functions: 0% (0 / 1)      Lines: 66.67% (2 / 3)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51                                1                                                       1            
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var nodes = {
    "irc in":     {module:"node-red-node-irc"},
    "irc out":    {module:"node-red-node-irc"},
    "irc-server": {module:"node-red-node-irc"},
    
    "arduino in":    {module:"node-red-node-arduino"},
    "arduino out":   {module:"node-red-node-arduino"},
    "arduino-board": {module:"node-red-node-arduino"},
    
    "redis out": {module:"node-red-node-redis"},
    
    "mongodb": {module:"node-red-node-mongodb"},
    "mongodb out": {module:"node-red-node-mongodb"},
    
    "serial in": {module:"node-red-node-serialport"},
    "serial out": {module:"node-red-node-serialport"},
    "serial-port": {module:"node-red-node-serialport"},
    
    "twitter-credentials": {module:"node-red-node-twitter"},
    "twitter in": {module:"node-red-node-twitter"},
    "twitter out": {module:"node-red-node-twitter"},
    
    "e-mail": {module:"node-red-node-email"},
    "e-mail in": {module:"node-red-node-email"},
    
    "feedparse": {module:"node-red-node-feedparser"}
}
 
module.exports = {
    get: function(id) {
        return nodes[id];
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/index.js

Statements: 70.37% (19 / 27)      Branches: 0% (0 / 2)      Functions: 28.57% (2 / 7)      Lines: 70.37% (19 / 27)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88                                1 1 1   1 1 1 1   1   1 1 1 1 1     1 1 1     1           1                       1                                                            
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var fs = require("fs");
var path = require("path");
 
var events = require("../../events");
var registry = require("./registry");
var loader = require("./loader");
var installer = require("./installer");
 
var settings;
 
function init(runtime) {
    settings = runtime.settings;
    installer.init(runtime.settings);
    loader.init(runtime);
    registry.init(settings,loader);
}
 
function load() {
    registry.load();
    return installer.checkPrereq().then(loader.load);
}
 
function addModule(module) {
    return loader.addModule(module).then(function() {
        return registry.getModuleInfo(module);
    });
}
 
function enableNodeSet(typeOrId) {
    return registry.enableNodeSet(typeOrId).then(function() {
        var nodeSet = registry.getNodeInfo(typeOrId);
        if (!nodeSet.loaded) {
            return loader.loadNodeSet(registry.getFullNodeInfo(typeOrId)).then(function() {
                return registry.getNodeInfo(typeOrId);
            });
        }
        return when.resolve(nodeSet);
    });
}
 
module.exports = {
    init:init,
    load:load,
    clear: registry.clear,
    registerType: registry.registerNodeConstructor,
 
    get: registry.getNodeConstructor,
    getNodeInfo: registry.getNodeInfo,
    getNodeList: registry.getNodeList,
 
    getModuleInfo: registry.getModuleInfo,
    getModuleList: registry.getModuleList,
 
    getNodeConfigs: registry.getAllNodeConfigs,
    getNodeConfig: registry.getNodeConfig,
 
    enableNode: enableNodeSet,
    disableNode: registry.disableNodeSet,
 
    addModule: addModule,
    removeModule: registry.removeModule,
 
    installModule: installer.installModule,
    uninstallModule: installer.uninstallModule,
 
    cleanModuleList: registry.cleanModuleList,
 
    paletteEditorEnabled: installer.paletteEditorEnabled
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/installer.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/installer.js

Statements: 25% (28 / 112)      Branches: 11.9% (5 / 42)      Functions: 26.67% (4 / 15)      Lines: 25% (28 / 112)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221                                  1 1 1   1 1   1   1 1 1   1   1 1   1 1     1                                           1                 1                                                                                                 1                                 1                     1                                                                               1 1                 1 1 1       1   1         1                    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
 
var when = require("when");
var path = require("path");
var fs = require("fs");
 
var registry = require("./registry");
var log = require("../../log");
 
var events = require("../../events");
 
var child_process = require('child_process');
var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
var paletteEditorEnabled = false;
 
var settings;
 
var moduleRe = /^[^/]+$/;
var slashRe = process.platform === "win32" ? /\\|[/]/ : /[/]/;
 
function init(_settings) {
    settings = _settings;
}
 
function checkModulePath(folder) {
    var moduleName;
    var err;
    var fullPath = path.resolve(folder);
    var packageFile = path.join(fullPath,'package.json');
    try {
        var pkg = require(packageFile);
        moduleName = pkg.name;
        if (!pkg['node-red']) {
            // TODO: nls
            err = new Error("Invalid Node-RED module");
            err.code = 'invalid_module';
            throw err;
        }
    } catch(err2) {
        err = new Error("Module not found");
        err.code = 404;
        throw err;
    }
    return moduleName;
}
 
function checkExistingModule(module) {
    if (registry.getModuleInfo(module)) {
        // TODO: nls
        var err = new Error("Module already loaded");
        err.code = "module_already_loaded";
        throw err;
    }
}
 
function installModule(module) {
    //TODO: ensure module is 'safe'
    return when.promise(function(resolve,reject) {
        var installName = module;
 
        try {
            if (moduleRe.test(module)) {
                // Simple module name - assume it can be npm installed
            } else if (slashRe.test(module)) {
                // A path - check if there's a valid package.json
                installName = module;
                module = checkModulePath(module);
            }
            checkExistingModule(module);
        } catch(err) {
            return reject(err);
        }
        log.info(log._("server.install.installing",{name: module}));
 
        var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
        var child = child_process.execFile(npmCommand,['install','--production',installName],
            {
                cwd: installDir
            },
            function(err, stdin, stdout) {
                if (err) {
                    var lookFor404 = new RegExp(" 404 .*"+installName+"$","m");
                    if (lookFor404.test(stdout)) {
                        log.warn(log._("server.install.install-failed-not-found",{name:module}));
                        var e = new Error("Module not found");
                        e.code = 404;
                        reject(e);
                    } else {
                        log.warn(log._("server.install.install-failed-long",{name:module}));
                        log.warn("------------------------------------------");
                        log.warn(err.toString());
                        log.warn("------------------------------------------");
                        reject(new Error(log._("server.install.install-failed")));
                    }
                } else {
                    log.info(log._("server.install.installed",{name:module}));
                    resolve(require("./index").addModule(module).then(reportAddedModules));
                }
            }
        );
    });
}
 
 
function reportAddedModules(info) {
    //comms.publish("node/added",info.nodes,false);
    if (info.nodes.length > 0) {
        log.info(log._("server.added-types"));
        for (var i=0;i<info.nodes.length;i++) {
            for (var j=0;j<info.nodes[i].types.length;j++) {
                log.info(" - "+
                    (info.nodes[i].module?info.nodes[i].module+":":"")+
                    info.nodes[i].types[j]+
                    (info.nodes[i].err?" : "+info.nodes[i].err:"")
                );
            }
        }
    }
    return info;
}
 
function reportRemovedModules(removedNodes) {
    //comms.publish("node/removed",removedNodes,false);
    log.info(log._("server.removed-types"));
    for (var j=0;j<removedNodes.length;j++) {
        for (var i=0;i<removedNodes[j].types.length;i++) {
            log.info(" - "+(removedNodes[j].module?removedNodes[j].module+":":"")+removedNodes[j].types[i]);
        }
    }
    return removedNodes;
}
 
function uninstallModule(module) {
    return when.promise(function(resolve,reject) {
        if (/[\s;]/.test(module)) {
            reject(new Error(log._("server.install.invalid")));
            return;
        }
        var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
        var moduleDir = path.join(installDir,"node_modules",module);
 
        try {
            fs.statSync(moduleDir);
        } catch(err) {
            return reject(new Error(log._("server.install.uninstall-failed",{name:module})));
        }
 
        var list = registry.removeModule(module);
        log.info(log._("server.install.uninstalling",{name:module}));
        var child = child_process.execFile(npmCommand,['remove',module],
            {
                cwd: installDir
            },
            function(err, stdin, stdout) {
                if (err) {
                    log.warn(log._("server.install.uninstall-failed-long",{name:module}));
                    log.warn("------------------------------------------");
                    log.warn(err.toString());
                    log.warn("------------------------------------------");
                    reject(new Error(log._("server.install.uninstall-failed",{name:module})));
                } else {
                    log.info(log._("server.install.uninstalled",{name:module}));
                    reportRemovedModules(list);
                    // TODO: tidy up internal event names
                    events.emit("node-module-uninstalled",module)
                    resolve(list);
                }
            }
        );
    });
}
 
function checkPrereq() {
    Iif (settings.hasOwnProperty('editorTheme') &&
        settings.editorTheme.hasOwnProperty('palette') &&
        settings.editorTheme.palette.hasOwnProperty('editable') &&
        settings.editorTheme.palette.editable === false
    ) {
        log.info(log._("server.palette-editor.disabled"));
        paletteEditorEnabled = false;
        return when.resolve();
    } else {
        return when.promise(function(resolve) {
            child_process.execFile(npmCommand,['-v'],function(err) {
                Iif (err) {
                    log.info(log._("server.palette-editor.npm-not-found"));
                    paletteEditorEnabled = false;
                } else {
                    paletteEditorEnabled = true;
                }
                resolve();
            });
        })
    }
}
module.exports = {
    init: init,
    checkPrereq: checkPrereq,
    installModule: installModule,
    uninstallModule: uninstallModule,
    paletteEditorEnabled: function() {
        return paletteEditorEnabled
    }
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/loader.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/loader.js

Statements: 72.99% (154 / 211)      Branches: 55.95% (47 / 84)      Functions: 70.37% (19 / 27)      Lines: 72.99% (154 / 211)      Ignored: 2 branches     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399                                1 1 1 1   1 1   1 1   1 1 1 1     1         1   1 1     1 111     111 37 222 222 222     74 74 1628 1480 1480           1 37               37 37 51   37 37 37 37 37 37 37 37 37                             37 2 2 2   2   37       1 1 1   5 5           5 5 5   37 37   4 4 4 4 4 12 4     4   4 4             37 37                 1 1 37 37   1       1 37 37 37 37 37   37 37 37 37 37     37     37                     37       37 37                         37   37 37   37 55   37   37 37 37 37 37 37 37 54 54 54   54 54     54 37     54   37   37 37     37 55         37 37 4 4       4     33 33                               1 37 37 37       37 37 37 37   37 37 36                     36 36 36 36   36   1 1       1 1 1 37 37           1 1 1             1                                     1                         1                                   1                
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var fs = require("fs");
var path = require("path");
var semver = require("semver");
 
var localfilesystem = require("./localfilesystem");
var registry = require("./registry");
 
var settings;
var runtime;
 
function init(_runtime) {
    runtime = _runtime;
    settings = runtime.settings;
    localfilesystem.init(runtime);
}
 
function load(defaultNodesDir,disableNodePathScan) {
    // To skip node scan, the following line will use the stored node list.
    // We should expose that as an option at some point, although the
    // performance gains are minimal.
    //return loadNodeFiles(registry.getModuleList());
    runtime.log.info(runtime.log._("server.loading"));
 
    var nodeFiles = localfilesystem.getNodeFiles(defaultNodesDir,disableNodePathScan);
    return loadNodeFiles(nodeFiles);
}
 
function copyObjectProperties(src,dst,copyList,blockList) {
    Iif (!src) {
        return;
    }
    if (copyList && !blockList) {
        copyList.forEach(function(i) {
            Eif (src.hasOwnProperty(i)) {
                var propDescriptor = Object.getOwnPropertyDescriptor(src,i);
                Object.defineProperty(dst,i,propDescriptor);
            }
        });
    } else Eif (!copyList && blockList) {
        for (var i in src) {
            if (src.hasOwnProperty(i) && blockList.indexOf(i) === -1) {
                var propDescriptor = Object.getOwnPropertyDescriptor(src,i);
                Object.defineProperty(dst,i,propDescriptor);
            }
        }
    }
}
 
function createNodeApi(node) {
    var red = {
        nodes: {},
        log: {},
        settings: {},
        events: runtime.events,
        util: runtime.util,
        version: runtime.version,
    }
    copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","addCredentials","getCredentials","deleteCredentials" ]);
    red.nodes.registerType = function(type,constructor,opts) {
        runtime.nodes.registerType(node.id,type,constructor,opts);
    }
    copyObjectProperties(runtime.log,red.log,null,["init"]);
    copyObjectProperties(runtime.settings,red.settings,null,["init","load","reset"]);
    Eif (runtime.adminApi) {
        red.comms = runtime.adminApi.comms;
        red.library = runtime.adminApi.library;
        red.auth = runtime.adminApi.auth;
        red.httpAdmin = runtime.adminApi.adminApp;
        red.httpNode = runtime.nodeApp;
        red.server = runtime.adminApi.server;
    } else {
        //TODO: runtime.adminApi is always stubbed if not enabled, so this block
        // is unused - but may be needed for the unit tests
        red.comms = {
            publish: function() {}
        };
        red.library = {
            register: function() {}
        };
        red.auth = {
            needsPermission: function() {}
        };
        // TODO: stub out httpAdmin/httpNode/server
    }
    red["_"] = function() {
        var args = Array.prototype.slice.call(arguments, 0);
        Eif (args[0].indexOf(":") === -1) {
            args[0] = node.namespace+":"+args[0];
        }
        return runtime.i18n._.apply(null,args);
    }
    return red;
}
 
 
function loadNodeFiles(nodeFiles) {
    var promises = [];
    for (var module in nodeFiles) {
        /* istanbul ignore else */
        Eif (nodeFiles.hasOwnProperty(module)) {
            Iif (nodeFiles[module].redVersion &&
                !semver.satisfies(runtime.version().replace("-git",""), nodeFiles[module].redVersion)) {
                //TODO: log it
                runtime.log.warn("["+module+"] "+runtime.log._("server.node-version-mismatch",{version:nodeFiles[module].redVersion}));
                continue;
            }
            Eif (module == "node-red" || !registry.getModuleInfo(module)) {
                var first = true;
                for (var node in nodeFiles[module].nodes) {
                    /* istanbul ignore else */
                    Eif (nodeFiles[module].nodes.hasOwnProperty(node)) {
                        if (module != "node-red" && first) {
                            // Check the module directory exists
                            first = false;
                            var fn = nodeFiles[module].nodes[node].file;
                            var parts = fn.split("/");
                            var i = parts.length-1;
                            for (;i>=0;i--) {
                                if (parts[i] == "node_modules") {
                                    break;
                                }
                            }
                            var moduleFn = parts.slice(0,i+2).join("/");
 
                            try {
                                var stat = fs.statSync(moduleFn);
                            } catch(err) {
                                // Module not found, don't attempt to load its nodes
                                break;
                            }
                        }
 
                        try {
                            promises.push(loadNodeConfig(nodeFiles[module].nodes[node]))
                        } catch(err) {
                            //
                        }
                    }
                }
            }
        }
    }
    return when.settle(promises).then(function(results) {
        var nodes = results.map(function(r) {
            registry.addNodeSet(r.value.id,r.value,r.value.version);
            return r.value;
        });
        return loadNodeSetList(nodes);
    });
}
 
function loadNodeConfig(fileInfo) {
    return when.promise(function(resolve) {
        var file = fileInfo.file;
        var module = fileInfo.module;
        var name = fileInfo.name;
        var version = fileInfo.version;
 
        var id = module + "/" + name;
        var info = registry.getNodeInfo(id);
        var isEnabled = true;
        Eif (info) {
            Iif (info.hasOwnProperty("loaded")) {
                throw new Error(file+" already loaded");
            }
            isEnabled = info.enabled;
        }
 
        var node = {
            id: id,
            module: module,
            name: name,
            file: file,
            template: file.replace(/\.js$/,".html"),
            enabled: isEnabled,
            loaded:false,
            version: version,
            local: fileInfo.local
        };
        Iif (fileInfo.hasOwnProperty("types")) {
            node.types = fileInfo.types;
        }
 
        fs.readFile(node.template,'utf8', function(err,content) {
            Iif (err) {
                node.types = [];
                if (err.code === 'ENOENT') {
                    if (!node.types) {
                        node.types = [];
                    }
                    node.err = "Error: "+node.template+" does not exist";
                } else {
                    node.types = [];
                    node.err = err.toString();
                }
                resolve(node);
            } else {
                var types = [];
 
                var regExp = /<script ([^>]*)data-template-name=['"]([^'"]*)['"]/gi;
                var match = null;
 
                while ((match = regExp.exec(content)) !== null) {
                    types.push(match[2]);
                }
                node.types = types;
 
                var langRegExp = /^<script[^>]* data-lang=['"](.+?)['"]/i;
                regExp = /(<script[^>]* data-help-name=[\s\S]*?<\/script>)/gi;
                match = null;
                var mainContent = "";
                var helpContent = {};
                var index = 0;
                while ((match = regExp.exec(content)) !== null) {
                    mainContent += content.substring(index,regExp.lastIndex-match[1].length);
                    index = regExp.lastIndex;
                    var help = content.substring(regExp.lastIndex-match[1].length,regExp.lastIndex);
 
                    var lang = runtime.i18n.defaultLang;
                    Iif ((match = langRegExp.exec(help)) !== null) {
                        lang = match[1];
                    }
                    if (!helpContent.hasOwnProperty(lang)) {
                        helpContent[lang] = "";
                    }
 
                    helpContent[lang] += help;
                }
                mainContent += content.substring(index);
 
                node.config = mainContent;
                node.help = helpContent;
                // TODO: parse out the javascript portion of the template
                //node.script = "";
                for (var i=0;i<node.types.length;i++) {
                    Iif (registry.getTypeId(node.types[i])) {
                        node.err = node.types[i]+" already registered";
                        break;
                    }
                }
                fs.stat(path.join(path.dirname(file),"locales"),function(err,stat) {
                    if (!err) {
                        node.namespace = node.id;
                        runtime.i18n.registerMessageCatalog(node.id,
                                path.join(path.dirname(file),"locales"),
                                path.basename(file,".js")+".json")
                            .then(function() {
                                resolve(node);
                            });
                    } else {
                        node.namespace = node.module;
                        resolve(node);
                    }
                });
            }
        });
    });
}
 
/**
 * Loads the specified node into the runtime
 * @param node a node info object - see loadNodeConfig
 * @return a promise that resolves to an update node info object. The object
 *         has the following properties added:
 *            err: any error encountered whilst loading the node
 *
 */
function loadNodeSet(node) {
    var nodeDir = path.dirname(node.file);
    var nodeFn = path.basename(node.file);
    Iif (!node.enabled) {
        return when.resolve(node);
    } else {
    }
    try {
        var loadPromise = null;
        var r = require(node.file);
        Eif (typeof r === "function") {
 
            var red = createNodeApi(node);
            var promise = r(red);
            Iif (promise != null && typeof promise.then === "function") {
                loadPromise = promise.then(function() {
                    node.enabled = true;
                    node.loaded = true;
                    return node;
                }).otherwise(function(err) {
                    node.err = err;
                    return node;
                });
            }
        }
        Eif (loadPromise == null) {
            node.enabled = true;
            node.loaded = true;
            loadPromise = when.resolve(node);
        }
        return loadPromise;
    } catch(err) {
        node.err = err;
        return when.resolve(node);
    }
}
 
function loadNodeSetList(nodes) {
    var promises = [];
    nodes.forEach(function(node) {
        Eif (!node.err) {
            promises.push(loadNodeSet(node));
        } else {
            promises.push(node);
        }
    });
 
    return when.settle(promises).then(function() {
        Eif (settings.available()) {
            return registry.saveNodeList();
        } else {
            return;
        }
    });
}
 
function addModule(module) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
    var nodes = [];
    if (registry.getModuleInfo(module)) {
        // TODO: nls
        var e = new Error("module_already_loaded");
        e.code = "module_already_loaded";
        return when.reject(e);
    }
    try {
        var moduleFiles = localfilesystem.getModuleFiles(module);
        return loadNodeFiles(moduleFiles);
    } catch(err) {
        return when.reject(err);
    }
}
 
function loadNodeHelp(node,lang) {
    var dir = path.dirname(node.template);
    var base = path.basename(node.template);
    var localePath = path.join(dir,"locales",lang,base);
    try {
        // TODO: make this async
        var content = fs.readFileSync(localePath, "utf8")
        return content;
    } catch(err) {
        return null;
    }
}
 
function getNodeHelp(node,lang) {
    if (!node.help[lang]) {
        var help = loadNodeHelp(node,lang);
        if (help == null) {
            var langParts = lang.split("-");
            if (langParts.length == 2) {
                help = loadNodeHelp(node,langParts[0]);
            }
        }
        if (help) {
            node.help[lang] = help;
        } else {
            node.help[lang] = node.help[runtime.i18n.defaultLang];
        }
    }
    return node.help[lang];
}
 
module.exports = {
    init: init,
    load: load,
    addModule: addModule,
    loadNodeSet: loadNodeSet,
    getNodeHelp: getNodeHelp
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/localfilesystem.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/localfilesystem.js

Statements: 79.08% (121 / 153)      Branches: 53.85% (35 / 65)      Functions: 75% (12 / 16)      Lines: 79.61% (121 / 152)      Ignored: 1 branch     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298                                1 1 1   1 1 1   1 1   1 1 1 1 1     1 349             349   1 33     33 33 33                                   1 10 10 10 10   1   9 9 81 81 70 33 33 33     11   11 8 3         9     1 9 9 9 2 316 316     316 316 316 316 315 4 4     1       316               9               1 1 1 1   1 1 1 1     1 1 1 8 8 8   8 8     1     1   4 4   4 4 4   4   4 4 4           4 4 4 4 1 1           4 4 4       4     1 1   1   1 1 1 1     1 1 1   1                   1             1 33     1 1 1 4 4           4     4 4 4   4         1     1                                                         1              
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require("when");
var fs = require("fs");
var path = require("path");
 
var events;
var log;
var i18n;
 
var settings;
var disableNodePathScan = false;
 
function init(runtime) {
    settings = runtime.settings;
    events = runtime.events;
    log = runtime.log;
    i18n = runtime.i18n;
}
 
function isExcluded(name) {
     Iif (settings.nodesExcludes) {
        for (var i=0;i<settings.nodesExcludes.length;i++) {
            if (settings.nodesExcludes[i] == name) {
                return true;
            }
        }
    }
    return false;
}
function getLocalFile(file) {
    Iif (isExcluded(path.basename(file))) {
        return null;
    }
    try {
        fs.statSync(file.replace(/\.js$/,".html"));
        return {
            file:    file,
            module:  "node-red",
            name:    path.basename(file).replace(/^\d+-/,"").replace(/\.js$/,""),
            version: settings.version
        };
    } catch(err) {
        return null;
    }
}
 
 
/**
 * Synchronously walks the directory looking for node files.
 * Emits 'node-icon-dir' events for an icon dirs found
 * @param dir the directory to search
 * @return an array of fully-qualified paths to .js files
 */
function getLocalNodeFiles(dir) {
    var result = [];
    var files = [];
    try {
        files = fs.readdirSync(dir);
    } catch(err) {
        return result;
    }
    files.sort();
    files.forEach(function(fn) {
        var stats = fs.statSync(path.join(dir,fn));
        if (stats.isFile()) {
            if (/\.js$/.test(fn)) {
                var info = getLocalFile(path.join(dir,fn));
                Eif (info) {
                    result.push(info);
                }
            }
        } else Eif (stats.isDirectory()) {
            // Ignore /.dirs/, /lib/ /node_modules/
            if (!/^(\..*|lib|icons|node_modules|test|locales)$/.test(fn)) {
                result = result.concat(getLocalNodeFiles(path.join(dir,fn)));
            } else Iif (fn === "icons") {
                events.emit("node-icon-dir",path.join(dir,fn));
            }
        }
    });
    return result;
}
 
function scanDirForNodesModules(dir,moduleName) {
    var results = [];
    try {
        var files = fs.readdirSync(dir);
        for (var i=0;i<files.length;i++) {
            var fn = files[i];
            Iif (/^@/.test(fn)) {
                results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
            } else {
                Eif (!isExcluded(fn) && (!moduleName || fn == moduleName)) {
                    var pkgfn = path.join(dir,fn,"package.json");
                    try {
                        var pkg = require(pkgfn);
                        if (pkg['node-red']) {
                            var moduleDir = path.join(dir,fn);
                            results.push({dir:moduleDir,package:pkg});
                        }
                    } catch(err) {
                        Iif (err.code != "MODULE_NOT_FOUND") {
                            // TODO: handle unexpected error
                        }
                    }
                    Iif (fn == moduleName) {
                        break;
                    }
                }
            }
        }
    } catch(err) {
    }
    return results;
}
 
/**
 * Scans the node_modules path for nodes
 * @param moduleName the name of the module to be found
 * @return a list of node modules: {dir,package}
 */
function scanTreeForNodesModules(moduleName) {
    var dir = settings.coreNodesDir;
    var results = [];
    var userDir;
 
    Eif (settings.userDir) {
        userDir = path.join(settings.userDir,"node_modules");
        results = scanDirForNodesModules(userDir,moduleName);
        results.forEach(function(r) { r.local = true; });
    }
 
    Eif (dir) {
        var up = path.resolve(path.join(dir,".."));
        while (up !== dir) {
            var pm = path.join(dir,"node_modules");
            Eif (pm != userDir) {
                results = results.concat(scanDirForNodesModules(pm,moduleName));
            }
            dir = up;
            up = path.resolve(path.join(dir,".."));
        }
    }
    return results;
}
 
function getModuleNodeFiles(module) {
 
    var moduleDir = module.dir;
    var pkg = module.package;
 
    var nodes = pkg['node-red'].nodes||{};
    var results = [];
    var iconDirs = [];
 
    for (var n in nodes) {
        /* istanbul ignore else */
        Eif (nodes.hasOwnProperty(n)) {
            var file = path.join(moduleDir,nodes[n]);
            results.push({
                file:    file,
                module:  pkg.name,
                name:    n,
                version: pkg.version
            });
            var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons");
            Eif (iconDirs.indexOf(iconDir) == -1) {
                try {
                    fs.statSync(iconDir);
                    events.emit("node-icon-dir",iconDir);
                    iconDirs.push(iconDir);
                } catch(err) {
                }
            }
        }
    }
    var examplesDir = path.join(moduleDir,"examples");
    try {
        fs.statSync(examplesDir)
        events.emit("node-examples-dir",{name:pkg.name,path:examplesDir});
    } catch(err) {
    }
    return results;
}
 
function getNodeFiles(disableNodePathScan) {
    var dir;
    // Find all of the nodes to load
    var nodeFiles = [];
 
    Eif (settings.coreNodesDir) {
        nodeFiles = getLocalNodeFiles(path.resolve(settings.coreNodesDir));
        var defaultLocalesPath = path.resolve(path.join(settings.coreNodesDir,"core","locales"));
        i18n.registerMessageCatalog("node-red",defaultLocalesPath,"messages.json");
    }
 
    Eif (settings.userDir) {
        dir = path.join(settings.userDir,"nodes");
        nodeFiles = nodeFiles.concat(getLocalNodeFiles(dir));
    }
    Iif (settings.nodesDir) {
        dir = settings.nodesDir;
        if (typeof settings.nodesDir == "string") {
            dir = [dir];
        }
        for (var i=0;i<dir.length;i++) {
            nodeFiles = nodeFiles.concat(getLocalNodeFiles(dir[i]));
        }
    }
 
    var nodeList = {
        "node-red": {
            name: "node-red",
            version: settings.version,
            nodes: {}
        }
    }
    nodeFiles.forEach(function(node) {
        nodeList["node-red"].nodes[node.name] = node;
    });
 
    Eif (!disableNodePathScan) {
        var moduleFiles = scanTreeForNodesModules();
        moduleFiles.forEach(function(moduleFile) {
            var nodeModuleFiles = getModuleNodeFiles(moduleFile);
            nodeList[moduleFile.package.name] = {
                name: moduleFile.package.name,
                version: moduleFile.package.version,
                local: moduleFile.local||false,
                nodes: {}
            };
            Iif (moduleFile.package['node-red'].version) {
                nodeList[moduleFile.package.name].redVersion = moduleFile.package['node-red'].version;
            }
            nodeModuleFiles.forEach(function(node) {
                node.local = moduleFile.local||false;
                nodeList[moduleFile.package.name].nodes[node.name] = node;
            });
            nodeFiles = nodeFiles.concat(nodeModuleFiles);
        });
    } else {
        console.log("node path scan disabled");
    }
    return nodeList;
}
 
function getModuleFiles(module) {
    var nodeList = {};
 
    var moduleFiles = scanTreeForNodesModules(module);
    if (moduleFiles.length === 0) {
        var err = new Error(log._("nodes.registry.localfilesystem.module-not-found", {module:module}));
        err.code = 'MODULE_NOT_FOUND';
        throw err;
    }
 
    moduleFiles.forEach(function(moduleFile) {
        var nodeModuleFiles = getModuleNodeFiles(moduleFile);
        nodeList[moduleFile.package.name] = {
            name: moduleFile.package.name,
            version: moduleFile.package.version,
            nodes: {}
        };
        if (moduleFile.package['node-red'].version) {
            nodeList[moduleFile.package.name].redVersion = moduleFile.package['node-red'].version;
        }
        nodeModuleFiles.forEach(function(node) {
            nodeList[moduleFile.package.name].nodes[node.name] = node;
            nodeList[moduleFile.package.name].nodes[node.name].local = moduleFile.local || false;
        });
    });
    return nodeList;
}
 
 
module.exports = {
    init: init,
    getNodeFiles: getNodeFiles,
    getLocalFile: getLocalFile,
    getModuleFiles: getModuleFiles
}
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/registry.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/nodes/registry/registry.js

Statements: 45.61% (135 / 296)      Branches: 38.57% (54 / 140)      Functions: 51.61% (16 / 31)      Lines: 45.76% (135 / 295)      Ignored: 10 branches     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577                                  1 1 1   1   1   1   1 1 1 1 1 1   1 1 1 1 1 1 1 1 1     1 1 1           1 148             148 148   148 3   148         1 88 88     1 88 88     1 1   1   5 5 5 5             5 5   37 37 37 37 37 37 37 37           1 1           1 1   1   1 1                                                                           1 37 37 55     37 37   37             37 37   37   37 37 37     1                                             1                                   1 37 37       37 37 37 37 37 37 37     37 37             1     51 51       51 51 51 51           1 2 2   10 10 10   74 74 74 74 1           2     1                         1 4                             4       1                       1 50 50                                     1 51           51 50     51 51 51                 51 51     1                                             1                                     1                               1               1 55     55       1                                             1                                           1                                                                             1                                                                    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
 //var UglifyJS = require("uglify-js");
var util = require("util");
var when = require("when");
var events = require("../../events");
 
var settings;
 
var Node;
 
var loader;
 
var nodeConfigCache = null;
var moduleConfigs = {};
var nodeList = [];
var nodeConstructors = {};
var nodeTypeToId = {};
var moduleNodes = {};
 
function init(_settings,_loader) {
    settings = _settings;
    loader = _loader;
    moduleNodes = {};
    nodeTypeToId = {};
    nodeConstructors = {};
    nodeList = [];
    nodeConfigCache = null;
    Node = require("../Node");
}
 
function load() {
    Eif (settings.available()) {
        moduleConfigs = loadNodeConfigs();
    } else {
        moduleConfigs = {};
    }
}
 
function filterNodeInfo(n) {
    var r = {
        id: n.id||n.module+"/"+n.name,
        name: n.name,
        types: n.types,
        enabled: n.enabled,
        local: n.local||false
    };
    Eif (n.hasOwnProperty("module")) {
        r.module = n.module;
    }
    if (n.hasOwnProperty("err")) {
        r.err = n.err.toString();
    }
    return r;
}
 
 
 
function getModule(id) {
    var parts = id.split("/");
    return parts.slice(0,parts.length-1).join("/");
}
 
function getNode(id) {
    var parts = id.split("/");
    return parts[parts.length-1];
}
 
function saveNodeList() {
    var moduleList = {};
 
    for (var module in moduleConfigs) {
        /* istanbul ignore else */
        Eif (moduleConfigs.hasOwnProperty(module)) {
            Eif (Object.keys(moduleConfigs[module].nodes).length > 0) {
                Eif (!moduleList[module]) {
                    moduleList[module] = {
                        name: module,
                        version: moduleConfigs[module].version,
                        local: moduleConfigs[module].local||false,
                        nodes: {}
                    };
                }
                var nodes = moduleConfigs[module].nodes;
                for(var node in nodes) {
                    /* istanbul ignore else */
                    Eif (nodes.hasOwnProperty(node)) {
                        var config = nodes[node];
                        var n = filterNodeInfo(config);
                        delete n.err;
                        delete n.file;
                        delete n.id;
                        n.file = config.file;
                        moduleList[module].nodes[node] = n;
                    }
                }
            }
        }
    }
    Eif (settings.available()) {
        return settings.set("nodes",moduleList);
    } else {
        return when.reject("Settings unavailable");
    }
}
 
function loadNodeConfigs() {
    var configs = settings.get("nodes");
 
    Iif (!configs) {
        return {};
    } else Eif (configs['node-red']) {
        return configs;
    } else {
        // Migrate from the 0.9.1 format of settings
        var newConfigs = {};
        for (var id in configs) {
            /* istanbul ignore else */
            if (configs.hasOwnProperty(id)) {
                var nodeConfig = configs[id];
                var moduleName;
                var nodeSetName;
 
                if (nodeConfig.module) {
                    moduleName = nodeConfig.module;
                    nodeSetName = nodeConfig.name.split(":")[1];
                } else {
                    moduleName = "node-red";
                    nodeSetName = nodeConfig.name.replace(/^\d+-/,"").replace(/\.js$/,"");
                }
 
                if (!newConfigs[moduleName]) {
                    newConfigs[moduleName] = {
                        name: moduleName,
                        nodes:{}
                    };
                }
                newConfigs[moduleName].nodes[nodeSetName] = {
                    name: nodeSetName,
                    types: nodeConfig.types,
                    enabled: nodeConfig.enabled,
                    module: moduleName
                };
            }
        }
        settings.set("nodes",newConfigs);
        return newConfigs;
    }
}
 
function addNodeSet(id,set,version) {
    Eif (!set.err) {
        set.types.forEach(function(t) {
            nodeTypeToId[t] = id;
        });
    }
    moduleNodes[set.module] = moduleNodes[set.module]||[];
    moduleNodes[set.module].push(set.name);
 
    Iif (!moduleConfigs[set.module]) {
        moduleConfigs[set.module] = {
            name: set.module,
            nodes: {}
        };
    }
 
    Eif (version) {
        moduleConfigs[set.module].version = version;
    }
    moduleConfigs[set.module].local = set.local;
 
    moduleConfigs[set.module].nodes[set.name] = set;
    nodeList.push(id);
    nodeConfigCache = null;
}
 
function removeNode(id) {
    var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
    if (!config) {
        throw new Error("Unrecognised id: "+id);
    }
    delete moduleConfigs[getModule(id)].nodes[getNode(id)];
    var i = nodeList.indexOf(id);
    if (i > -1) {
        nodeList.splice(i,1);
    }
    config.types.forEach(function(t) {
        var typeId = nodeTypeToId[t];
        if (typeId === id) {
            delete nodeConstructors[t];
            delete nodeTypeToId[t];
        }
    });
    config.enabled = false;
    config.loaded = false;
    nodeConfigCache = null;
    return filterNodeInfo(config);
}
 
function removeModule(module) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
    var nodes = moduleNodes[module];
    if (!nodes) {
        throw new Error("Unrecognised module: "+module);
    }
    var infoList = [];
    for (var i=0;i<nodes.length;i++) {
        infoList.push(removeNode(module+"/"+nodes[i]));
    }
    delete moduleNodes[module];
    delete moduleConfigs[module];
    saveNodeList();
    return infoList;
}
 
function getNodeInfo(typeOrId) {
    var id = typeOrId;
    Iif (nodeTypeToId.hasOwnProperty(typeOrId)) {
        id = nodeTypeToId[typeOrId];
    }
    /* istanbul ignore else */
    Eif (id) {
        var module = moduleConfigs[getModule(id)];
        Eif (module) {
            var config = module.nodes[getNode(id)];
            Eif (config) {
                var info = filterNodeInfo(config);
                Iif (config.hasOwnProperty("loaded")) {
                    info.loaded = config.loaded;
                }
                info.version = module.version;
                return info;
            }
        }
    }
    return null;
}
 
function getFullNodeInfo(typeOrId) {
    // Used by index.enableNodeSet so that .file can be retrieved to pass
    // to loader.loadNodeSet
    var id = typeOrId;
    Iif (nodeTypeToId.hasOwnProperty(typeOrId)) {
        id = nodeTypeToId[typeOrId];
    }
    /* istanbul ignore else */
    Eif (id) {
        var module = moduleConfigs[getModule(id)];
        Eif (module) {
            return module.nodes[getNode(id)];
        }
    }
    return null;
}
 
function getNodeList(filter) {
    var list = [];
    for (var module in moduleConfigs) {
        /* istanbul ignore else */
        Eif (moduleConfigs.hasOwnProperty(module)) {
            var nodes = moduleConfigs[module].nodes;
            for (var node in nodes) {
                /* istanbul ignore else */
                Eif (nodes.hasOwnProperty(node)) {
                    var nodeInfo = filterNodeInfo(nodes[node]);
                    nodeInfo.version = moduleConfigs[module].version;
                    if (!filter || filter(nodes[node])) {
                        list.push(nodeInfo);
                    }
                }
            }
        }
    }
    return list;
}
 
function getModuleList() {
    //var list = [];
    //for (var module in moduleNodes) {
    //    /* istanbul ignore else */
    //    if (moduleNodes.hasOwnProperty(module)) {
    //        list.push(registry.getModuleInfo(module));
    //    }
    //}
    //return list;
    return moduleConfigs;
 
}
 
function getModuleInfo(module) {
    Iif (moduleNodes[module]) {
        var nodes = moduleNodes[module];
        var m = {
            name: module,
            version: moduleConfigs[module].version,
            local: moduleConfigs[module].local,
            nodes: []
        };
        for (var i = 0; i < nodes.length; ++i) {
            var nodeInfo = filterNodeInfo(moduleConfigs[module].nodes[nodes[i]]);
            nodeInfo.version = m.version;
            m.nodes.push(nodeInfo);
        }
        return m;
    } else {
        return null;
    }
}
 
function getCaller(){
    var orig = Error.prepareStackTrace;
    Error.prepareStackTrace = function(_, stack){ return stack; };
    var err = new Error();
    Error.captureStackTrace(err, arguments.callee);
    var stack = err.stack;
    Error.prepareStackTrace = orig;
    stack.shift();
    stack.shift();
    return stack[0].getFileName();
}
 
function inheritNode(constructor) {
    Eif(Object.getPrototypeOf(constructor.prototype) === Object.prototype) {
        util.inherits(constructor,Node);
    } else {
        var proto = constructor.prototype;
        while(Object.getPrototypeOf(proto) !== Object.prototype) {
            proto = Object.getPrototypeOf(proto);
        }
        //TODO: This is a partial implementation of util.inherits >= node v5.0.0
        //      which should be changed when support for node < v5.0.0 is dropped
        //      see: https://github.com/nodejs/node/pull/3455
        proto.constructor.super_ = Node;
        if(Object.setPrototypeOf) {
            Object.setPrototypeOf(proto, Node.prototype);
        } else {
            // hack for node v0.10
            proto.__proto__ = Node.prototype;
        }
    }
}
 
function registerNodeConstructor(nodeSet,type,constructor) {
    Iif (nodeConstructors.hasOwnProperty(type)) {
        throw new Error(type+" already registered");
    }
    //TODO: Ensure type is known - but doing so will break some tests
    //      that don't have a way to register a node template ahead
    //      of registering the constructor
    if(!(constructor.prototype instanceof Node)) {
        inheritNode(constructor);
    }
 
    var nodeSetInfo = getFullNodeInfo(nodeSet);
    Eif (nodeSetInfo) {
        Iif (nodeSetInfo.types.indexOf(type) === -1) {
            // A type is being registered for a known set, but for some reason
            // we didn't spot it when parsing the HTML file.
            // Registered a type is the definitive action - not the presence
            // of an edit template. Ensure it is on the list of known types.
            nodeSetInfo.types.push(type);
        }
    }
 
    nodeConstructors[type] = constructor;
    events.emit("type-registered",type);
}
 
function getAllNodeConfigs(lang) {
    if (!nodeConfigCache) {
        var result = "";
        var script = "";
        for (var i=0;i<nodeList.length;i++) {
            var id = nodeList[i];
            var config = moduleConfigs[getModule(id)].nodes[getNode(id)];
            if (config.enabled && !config.err) {
                result += config.config;
                result += loader.getNodeHelp(config,lang||"en-US")||"";
                //script += config.script;
            }
        }
        //if (script.length > 0) {
        //    result += '<script type="text/javascript">';
        //    result += UglifyJS.minify(script, {fromString: true}).code;
        //    result += '</script>';
        //}
        nodeConfigCache = result;
    }
    return nodeConfigCache;
}
 
function getNodeConfig(id,lang) {
    var config = moduleConfigs[getModule(id)];
    if (!config) {
        return null;
    }
    config = config.nodes[getNode(id)];
    if (config) {
        var result = config.config;
        result += loader.getNodeHelp(config,lang||"en-US")
 
        //if (config.script) {
        //    result += '<script type="text/javascript">'+config.script+'</script>';
        //}
        return result;
    } else {
        return null;
    }
}
 
function getNodeConstructor(type) {
    var id = nodeTypeToId[type];
 
    var config;
    if (typeof id === "undefined") {
        config = undefined;
    } else {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
    }
 
    if (!config || (config.enabled && !config.err)) {
        return nodeConstructors[type];
    }
    return null;
}
 
function clear() {
    nodeConfigCache = null;
    moduleConfigs = {};
    nodeList = [];
    nodeConstructors = {};
    nodeTypeToId = {};
}
 
function getTypeId(type) {
    Iif (nodeTypeToId.hasOwnProperty(type)) {
        return nodeTypeToId[type];
    } else {
        return null;
    }
}
 
function enableNodeSet(typeOrId) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
 
    var id = typeOrId;
    if (nodeTypeToId.hasOwnProperty(typeOrId)) {
        id = nodeTypeToId[typeOrId];
    }
    var config;
    try {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
        delete config.err;
        config.enabled = true;
        nodeConfigCache = null;
        return saveNodeList().then(function() {
            return filterNodeInfo(config);
        });
    } catch (err) {
        throw new Error("Unrecognised id: "+typeOrId);
    }
}
 
function disableNodeSet(typeOrId) {
    if (!settings.available()) {
        throw new Error("Settings unavailable");
    }
    var id = typeOrId;
    if (nodeTypeToId.hasOwnProperty(typeOrId)) {
        id = nodeTypeToId[typeOrId];
    }
    var config;
    try {
        config = moduleConfigs[getModule(id)].nodes[getNode(id)];
        // TODO: persist setting
        config.enabled = false;
        nodeConfigCache = null;
        return saveNodeList().then(function() {
            return filterNodeInfo(config);
        });
    } catch (err) {
        throw new Error("Unrecognised id: "+id);
    }
}
 
function cleanModuleList() {
    var removed = false;
    for (var mod in moduleConfigs) {
        /* istanbul ignore else */
        if (moduleConfigs.hasOwnProperty(mod)) {
            var nodes = moduleConfigs[mod].nodes;
            var node;
            if (mod == "node-red") {
                // For core nodes, look for nodes that are enabled, !loaded and !errored
                for (node in nodes) {
                    /* istanbul ignore else */
                    if (nodes.hasOwnProperty(node)) {
                        var n = nodes[node];
                        if (n.enabled && !n.err && !n.loaded) {
                            removeNode(mod+"/"+node);
                            removed = true;
                        }
                    }
                }
            } else {
                if (moduleConfigs[mod] && !moduleNodes[mod]) {
                    // For node modules, look for missing ones
                    for (node in nodes) {
                        /* istanbul ignore else */
                        if (nodes.hasOwnProperty(node)) {
                            removeNode(mod+"/"+node);
                            removed = true;
                        }
                    }
                    delete moduleConfigs[mod];
                }
            }
        }
    }
    if (removed) {
        saveNodeList();
    }
}
 
var registry = module.exports = {
    init: init,
    load: load,
    clear: clear,
 
    registerNodeConstructor: registerNodeConstructor,
    getNodeConstructor: getNodeConstructor,
 
    addNodeSet: addNodeSet,
    enableNodeSet: enableNodeSet,
    disableNodeSet: disableNodeSet,
 
    removeModule: removeModule,
 
    getNodeInfo: getNodeInfo,
    getFullNodeInfo: getFullNodeInfo,
    getNodeList: getNodeList,
    getModuleList: getModuleList,
    getModuleInfo: getModuleInfo,
 
    /**
     * Gets all of the node template configs
     * @return all of the node templates in a single string
     */
    getAllNodeConfigs: getAllNodeConfigs,
    getNodeConfig: getNodeConfig,
 
    getTypeId: getTypeId,
 
    saveNodeList: saveNodeList,
 
    cleanModuleList: cleanModuleList
};
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/storage/

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/storage/

Statements: 30.18% (99 / 328)      Branches: 12.9% (16 / 124)      Functions: 26.79% (15 / 56)      Lines: 30.18% (99 / 328)      Ignored: none     

All files » node-npmtest-node-red/node_modules/node-red/red/runtime/storage/
File Statements Branches Functions Lines
index.js 29.13% (30 / 103) 13.64% (6 / 44) 25% (6 / 24) 29.13% (30 / 103)
localfilesystem.js 30.67% (69 / 225) 12.5% (10 / 80) 28.13% (9 / 32) 30.67% (69 / 225)
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/storage/index.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/storage/index.js

Statements: 29.13% (30 / 103)      Branches: 13.64% (6 / 44)      Functions: 25% (6 / 24)      Lines: 29.13% (30 / 103)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220                                1 1 1   1   1 1 1 1   1 1 1               1   1     1       1   1 1 1 1 1       1     1 1 1       1 1                                                       1 1                                                                                                                                                                       1                                                                           1    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var when = require('when');
var Path = require('path');
var crypto = require('crypto');
 
var log = require("../log");
 
var runtime;
var storageModule;
var settingsAvailable;
var sessionsAvailable;
 
function moduleSelector(aSettings) {
    var toReturn;
    Iif (aSettings.storageModule) {
        if (typeof aSettings.storageModule === "string") {
            // TODO: allow storage modules to be specified by absolute path
            toReturn = require("./"+aSettings.storageModule);
        } else {
            toReturn = aSettings.storageModule;
        }
    } else {
        toReturn = require("./localfilesystem");
    }
    return toReturn;
}
 
function is_malicious(path) {
    return path.indexOf('../') != -1 || path.indexOf('..\\') != -1;
}
 
var storageModuleInterface = {
        init: function(_runtime) {
            runtime = _runtime;
            try {
                storageModule = moduleSelector(runtime.settings);
                settingsAvailable = storageModule.hasOwnProperty("getSettings") && storageModule.hasOwnProperty("saveSettings");
                sessionsAvailable = storageModule.hasOwnProperty("getSessions") && storageModule.hasOwnProperty("saveSessions");
            } catch (e) {
                return when.reject(e);
            }
            return storageModule.init(runtime.settings);
        },
        getFlows: function() {
            return storageModule.getFlows().then(function(flows) {
                return storageModule.getCredentials().then(function(creds) {
                    var result = {
                        flows: flows,
                        credentials: creds
                    };
                    result.rev = crypto.createHash('md5').update(JSON.stringify(result)).digest("hex");
                    return result;
                })
            });
        },
        saveFlows: function(config) {
            var flows = config.flows;
            var credentials = config.credentials;
            var credentialSavePromise;
            if (config.credentialsDirty) {
                credentialSavePromise = storageModule.saveCredentials(credentials);
            } else {
                credentialSavePromise = when.resolve();
            }
            delete config.credentialsDirty;
 
            return credentialSavePromise.then(function() {
                return storageModule.saveFlows(flows).then(function() {
                    return crypto.createHash('md5').update(JSON.stringify(config)).digest("hex");
                })
            });
        },
        // getCredentials: function() {
        //     return storageModule.getCredentials();
        // },
        // saveCredentials: function(credentials) {
        //     return storageModule.saveCredentials(credentials);
        // },
        getSettings: function() {
            Eif (settingsAvailable) {
                return storageModule.getSettings();
            } else {
                return when.resolve(null);
            }
        },
        saveSettings: function(settings) {
            if (settingsAvailable) {
                return storageModule.saveSettings(settings);
            } else {
                return when.resolve();
            }
        },
        getSessions: function() {
            if (sessionsAvailable) {
                return storageModule.getSessions();
            } else {
                return when.resolve(null);
            }
        },
        saveSessions: function(sessions) {
            if (sessionsAvailable) {
                return storageModule.saveSessions(sessions);
            } else {
                return when.resolve();
            }
        },
 
        /* Library Functions */
 
        getLibraryEntry: function(type, path) {
            if (is_malicious(path)) {
                var err = new Error();
                err.code = "forbidden";
                return when.reject(err);
            }
            return storageModule.getLibraryEntry(type, path);
        },
        saveLibraryEntry: function(type, path, meta, body) {
            if (is_malicious(path)) {
                var err = new Error();
                err.code = "forbidden";
                return when.reject(err);
            }
            return storageModule.saveLibraryEntry(type, path, meta, body);
        },
 
/* Deprecated functions */
        getAllFlows: function() {
            if (storageModule.hasOwnProperty("getAllFlows")) {
                return storageModule.getAllFlows();
            } else {
                return listFlows("/");
            }
        },
        getFlow: function(fn) {
            if (is_malicious(fn)) {
                var err = new Error();
                err.code = "forbidden";
                return when.reject(err);
            }
            if (storageModule.hasOwnProperty("getFlow")) {
                return storageModule.getFlow(fn);
            } else {
                return storageModule.getLibraryEntry("flows",fn);
            }
 
        },
        saveFlow: function(fn, data) {
            if (is_malicious(fn)) {
                var err = new Error();
                err.code = "forbidden";
                return when.reject(err);
            }
            if (storageModule.hasOwnProperty("saveFlow")) {
                return storageModule.saveFlow(fn, data);
            } else {
                return storageModule.saveLibraryEntry("flows",fn,{},data);
            }
        }
/* End deprecated functions */
 
}
 
 
function listFlows(path) {
    return storageModule.getLibraryEntry("flows",path).then(function(res) {
        return when.promise(function(resolve) {
            var promises = [];
            res.forEach(function(r) {
                if (typeof r === "string") {
                    promises.push(listFlows(Path.join(path,r)));
                } else {
                    promises.push(when.resolve(r));
                }
            });
            var i=0;
            when.settle(promises).then(function(res2) {
                var result = {};
                res2.forEach(function(r) {
                    // TODO: name||fn
                    if (r.value.fn) {
                        var name = r.value.name;
                        if (!name) {
                            name = r.value.fn.split(".")[0];
                        }
                        result.f = result.f || [];
                        result.f.push(name);
                    } else {
                        result.d = result.d || {};
                        result.d[res[i]] = r.value;
                        //console.log(">",r.value);
                    }
                    i++;
                });
                resolve(result);
            });
        });
    });
}
 
 
 
module.exports = storageModuleInterface;
 
 
Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/storage/localfilesystem.js

npmtest-node-red (v0.0.2)

Code coverage report for node-npmtest-node-red/node_modules/node-red/red/runtime/storage/localfilesystem.js

Statements: 30.67% (69 / 225)      Branches: 12.5% (10 / 80)      Functions: 28.13% (9 / 32)      Lines: 30.67% (69 / 225)      Ignored: none     

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410                                1 1 1 1 1 1   1   1   1 1 1 1 1 1 1 1 1 1 1 1   1                                                       1                                                                                   1                             1 2 2 2                                                                   2 1   2           1   1   1   1 1 1     1 1 1         1                                         1 1   1 1 1 1   1 1   1   1   1   1 1   1   1 1     1       1 1 1 1   1                                               1                                           1 1 1 1 1                                                                                                                                                                                                                               1    
/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/
 
var fs = require('fs-extra');
var when = require('when');
var nodeFn = require('when/node/function');
var keys = require('when/keys');
var fspath = require("path");
var mkdirp = fs.mkdirs;
 
var log = require("../log");
 
var promiseDir = nodeFn.lift(mkdirp);
 
var initialFlowLoadComplete = false;
var settings;
var flowsFile;
var flowsFullPath;
var flowsFileBackup;
var credentialsFile;
var credentialsFileBackup;
var oldCredentialsFile;
var sessionsFile;
var libDir;
var libFlowsDir;
var globalSettingsFile;
 
function getFileMeta(root,path) {
    var fn = fspath.join(root,path);
    var fd = fs.openSync(fn,"r");
    var size = fs.fstatSync(fd).size;
    var meta = {};
    var read = 0;
    var length = 10;
    var remaining = "";
    var buffer = Buffer(length);
    while(read < size) {
        read+=fs.readSync(fd,buffer,0,length);
        var data = remaining+buffer.toString();
        var parts = data.split("\n");
        remaining = parts.splice(-1);
        for (var i=0;i<parts.length;i+=1) {
            var match = /^\/\/ (\w+): (.*)/.exec(parts[i]);
            if (match) {
                meta[match[1]] = match[2];
            } else {
                read = size;
                break;
            }
        }
    }
    fs.closeSync(fd);
    return meta;
}
 
function getFileBody(root,path) {
    var body = "";
    var fn = fspath.join(root,path);
    var fd = fs.openSync(fn,"r");
    var size = fs.fstatSync(fd).size;
    var scanning = true;
    var read = 0;
    var length = 50;
    var remaining = "";
    var buffer = Buffer(length);
    while(read < size) {
        var thisRead = fs.readSync(fd,buffer,0,length);
        read += thisRead;
        if (scanning) {
            var data = remaining+buffer.slice(0,thisRead).toString();
            var parts = data.split("\n");
            remaining = parts.splice(-1)[0];
            for (var i=0;i<parts.length;i+=1) {
                if (! /^\/\/ \w+: /.test(parts[i])) {
                    scanning = false;
                    body += parts[i]+"\n";
                }
            }
            if (! /^\/\/ \w+: /.test(remaining)) {
                scanning = false;
            }
            if (!scanning) {
                body += remaining;
            }
        } else {
            body += buffer.slice(0,thisRead).toString();
        }
    }
    fs.closeSync(fd);
    return body;
}
 
/**
 * Write content to a file using UTF8 encoding.
 * This forces a fsync before completing to ensure
 * the write hits disk.
 */
function writeFile(path,content) {
    return when.promise(function(resolve,reject) {
        var stream = fs.createWriteStream(path);
        stream.on('open',function(fd) {
            stream.end(content,'utf8',function() {
                fs.fsync(fd,resolve);
            });
        });
        stream.on('error',function(err) {
            reject(err);
        });
    });
}
 
 
function readFile(path,backupPath,emptyResponse,type) {
    return when.promise(function(resolve) {
        fs.readFile(path,'utf8',function(err,data) {
            Iif (!err) {
                if (data.length === 0) {
                    log.warn(log._("storage.localfilesystem.empty",{type:type}));
                    try {
                        var backupStat = fs.statSync(backupPath);
                        if (backupStat.size === 0) {
                            // Empty flows, empty backup - return empty flow
                            return resolve(emptyResponse);
                        }
                        // Empty flows, restore backup
                        log.warn(log._("storage.localfilesystem.restore",{path:backupPath,type:type}));
                        fs.copy(backupPath,path,function(backupCopyErr) {
                            if (backupCopyErr) {
                                // Restore backup failed
                                log.warn(log._("storage.localfilesystem.restore-fail",{message:backupCopyErr.toString(),type:type}));
                                resolve([]);
                            } else {
                                // Loop back in to load the restored backup
                                resolve(readFile(path,backupPath,emptyResponse,type));
                            }
                        });
                        return;
                    } catch(backupStatErr) {
                        // Empty flow file, no back-up file
                        return resolve(emptyResponse);
                    }
                }
                try {
                    return resolve(JSON.parse(data));
                } catch(parseErr) {
                    log.warn(log._("storage.localfilesystem.invalid",{type:type}));
                    return resolve(emptyResponse);
                }
            } else {
                if (type === 'flow') {
                    log.info(log._("storage.localfilesystem.create",{type:type}));
                }
                resolve(emptyResponse);
            }
        });
    });
}
 
var localfilesystem = {
    init: function(_settings) {
        settings = _settings;
 
        var promises = [];
 
        Eif (!settings.userDir) {
            try {
                fs.statSync(fspath.join(process.env.NODE_RED_HOME,".config.json"));
                settings.userDir = process.env.NODE_RED_HOME;
            } catch(err) {
                settings.userDir = fspath.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE || process.env.NODE_RED_HOME,".node-red");
                Eif (!settings.readOnly) {
                    promises.push(promiseDir(fspath.join(settings.userDir,"node_modules")));
                }
            }
        }
 
        Iif (settings.flowFile) {
            flowsFile = settings.flowFile;
            // handle Unix and Windows "C:\"
            if ((flowsFile[0] == "/") || (flowsFile[1] == ":")) {
                // Absolute path
                flowsFullPath = flowsFile;
            } else if (flowsFile.substring(0,2) === "./") {
                // Relative to cwd
                flowsFullPath = fspath.join(process.cwd(),flowsFile);
            } else {
                try {
                    fs.statSync(fspath.join(process.cwd(),flowsFile));
                    // Found in cwd
                    flowsFullPath = fspath.join(process.cwd(),flowsFile);
                } catch(err) {
                    // Use userDir
                    flowsFullPath = fspath.join(settings.userDir,flowsFile);
                }
            }
 
        } else {
            flowsFile = 'flows_'+require('os').hostname()+'.json';
            flowsFullPath = fspath.join(settings.userDir,flowsFile);
        }
        var ffExt = fspath.extname(flowsFullPath);
        var ffName = fspath.basename(flowsFullPath);
        var ffBase = fspath.basename(flowsFullPath,ffExt);
        var ffDir = fspath.dirname(flowsFullPath);
 
        credentialsFile = fspath.join(settings.userDir,ffBase+"_cred"+ffExt);
        credentialsFileBackup = fspath.join(settings.userDir,"."+ffBase+"_cred"+ffExt+".backup");
 
        oldCredentialsFile = fspath.join(settings.userDir,"credentials.json");
 
        flowsFileBackup = fspath.join(ffDir,"."+ffName+".backup");
 
        sessionsFile = fspath.join(settings.userDir,".sessions.json");
 
        libDir = fspath.join(settings.userDir,"lib");
        libFlowsDir = fspath.join(libDir,"flows");
 
        globalSettingsFile = fspath.join(settings.userDir,".config.json");
 
        Eif (!settings.readOnly) {
            promises.push(promiseDir(libFlowsDir));
        }
 
        return when.all(promises);
    },
 
    getFlows: function() {
        Eif (!initialFlowLoadComplete) {
            initialFlowLoadComplete = true;
            log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
            log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
        }
        return readFile(flowsFullPath,flowsFileBackup,[],'flow');
    },
 
    saveFlows: function(flows) {
        if (settings.readOnly) {
            return when.resolve();
        }
 
        try {
            fs.renameSync(flowsFullPath,flowsFileBackup);
        } catch(err) {
        }
 
        var flowData;
 
        if (settings.flowFilePretty) {
            flowData = JSON.stringify(flows,null,4);
        } else {
            flowData = JSON.stringify(flows);
        }
        return writeFile(flowsFullPath, flowData);
    },
 
    getCredentials: function() {
        return readFile(credentialsFile,credentialsFileBackup,{},'credentials');
    },
 
    saveCredentials: function(credentials) {
        if (settings.readOnly) {
            return when.resolve();
        }
 
        try {
            fs.renameSync(credentialsFile,credentialsFileBackup);
        } catch(err) {
        }
        var credentialData;
        if (settings.flowFilePretty) {
            credentialData = JSON.stringify(credentials,null,4);
        } else {
            credentialData = JSON.stringify(credentials);
        }
        return writeFile(credentialsFile, credentialData);
    },
 
    getSettings: function() {
        return when.promise(function(resolve,reject) {
            fs.readFile(globalSettingsFile,'utf8',function(err,data) {
                Eif (!err) {
                    try {
                        return resolve(JSON.parse(data));
                    } catch(err2) {
                        log.trace("Corrupted config detected - resetting");
                    }
                }
                return resolve({});
            })
        })
    },
    saveSettings: function(newSettings) {
        if (settings.readOnly) {
            return when.resolve();
        }
        return writeFile(globalSettingsFile,JSON.stringify(newSettings,null,1));
    },
    getSessions: function() {
        return when.promise(function(resolve,reject) {
            fs.readFile(sessionsFile,'utf8',function(err,data){
                if (!err) {
                    try {
                        return resolve(JSON.parse(data));
                    } catch(err2) {
                        log.trace("Corrupted sessions file - resetting");
                    }
                }
                resolve({});
            })
        });
    },
    saveSessions: function(sessions) {
        if (settings.readOnly) {
            return when.resolve();
        }
        return writeFile(sessionsFile,JSON.stringify(sessions));
    },
 
    getLibraryEntry: function(type,path) {
        var root = fspath.join(libDir,type);
        var rootPath = fspath.join(libDir,type,path);
 
        // don't create the folder if it does not exist - we are only reading....
        return nodeFn.call(fs.lstat, rootPath).then(function(stats) {
            if (stats.isFile()) {
                return getFileBody(root,path);
            }
            if (path.substr(-1) == '/') {
                path = path.substr(0,path.length-1);
            }
            return nodeFn.call(fs.readdir, rootPath).then(function(fns) {
                var dirs = [];
                var files = [];
                fns.sort().filter(function(fn) {
                    var fullPath = fspath.join(path,fn);
                    var absoluteFullPath = fspath.join(root,fullPath);
                    if (fn[0] != ".") {
                        var stats = fs.lstatSync(absoluteFullPath);
                        if (stats.isDirectory()) {
                            dirs.push(fn);
                        } else {
                            var meta = getFileMeta(root,fullPath);
                            meta.fn = fn;
                            files.push(meta);
                        }
                    }
                });
                return dirs.concat(files);
            });
        }).otherwise(function(err) {
            // if path is empty, then assume it was a folder, return empty
            if (path === ""){
                return [];
            }
 
            // if path ends with slash, it was a folder
            // so return empty
            if (path.substr(-1) == '/') {
                return [];
            }
 
            // else path was specified, but did not exist,
            // check for path.json as an alternative if flows
            if (type === "flows" && !/\.json$/.test(path)) {
                return localfilesystem.getLibraryEntry(type,path+".json")
                .otherwise(function(e) {
                    throw err;
                });
            } else {
                throw err;
            }
        });
    },
 
    saveLibraryEntry: function(type,path,meta,body) {
        if (settings.readOnly) {
            return when.resolve();
        }
        var fn = fspath.join(libDir, type, path);
        var headers = "";
        for (var i in meta) {
            if (meta.hasOwnProperty(i)) {
                headers += "// "+i+": "+meta[i]+"\n";
            }
        }
        if (type === "flows" && settings.flowFilePretty) {
            body = JSON.stringify(JSON.parse(body),null,4);
        }
        return promiseDir(fspath.dirname(fn)).then(function () {
            writeFile(fn,headers+body);
        });
    }
};
 
module.exports = localfilesystem;